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_ALSA 0
#define HAVE_PORTAUDIO 0
#define HAVE_SFML 1
// 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.

View File

@ -415,7 +415,12 @@ u32 CEXIIPL::GetGCTime()
u64 ltime = Common::Timer::GetTimeSinceJan1970();
return ((u32)ltime - cJanuary2000 - Bias);
#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)
return ((u32)ltime - cJanuary2000 - cWiiBias/* + 32434790*/);
else

View File

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

View File

@ -923,185 +923,15 @@
Name="NetPlay"
>
<File
RelativePath=".\Src\NetEvent.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\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"
RelativePath=".\Src\LockingQueue.h"
>
</File>
<File
RelativePath=".\Src\NetStructs.h"
RelativePath=".\Src\NetPlay.cpp"
>
</File>
<File
RelativePath=".\Src\NetPlay.h"
>
</File>
<File

View File

@ -187,8 +187,7 @@ void CFrame::CreateMenu()
toolsMenu->Append(IDM_CHEATS, _T("Action &Replay Manager"));
#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
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))
{
#if defined(HAVE_SFML) && HAVE_SFML
new NetPlay(this, m_GameListCtrl->GetGamePaths(), m_GameListCtrl->GetGameNames());
new NetPlaySetupDiag(this, m_GameListCtrl);
#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
// http://code.google.com/p/dolphin-emu/
#include "NetSockets.h"
#include "NetPlay.h"
#include "NetWindow.h"
#include "FileUtil.h"
// Main Frame window
#include <sstream>
BEGIN_EVENT_TABLE(NetPlay, wxFrame)
EVT_BUTTON(ID_BUTTON_JOIN, NetPlay::OnJoin)
EVT_BUTTON(ID_BUTTON_HOST, NetPlay::OnHost)
EVT_BUTTON(ID_BUTTON_EXIT, NetPlay::OnDisconnect)
#define _connect_macro_( b, f, c, s ) (b)->Connect( wxID_ANY, (c), wxCommandEventHandler( f ), (wxObject*)0, (wxEvtHandler*)s )
EVT_HOST_COMMAND(wxID_ANY, NetPlay::OnNetEvent)
#define NETPLAY_TITLEBAR "Dolphin NetPlay"
EVT_CHECKBOX(ID_READY, NetPlay::OnGUIEvent)
EVT_CHECKBOX(ID_RECORD, NetPlay::OnGUIEvent)
EVT_BUTTON(ID_CHANGEGAME, NetPlay::OnGUIEvent)
EVT_BUTTON(ID_BUTTON_GETIP, NetPlay::OnGUIEvent)
EVT_BUTTON(ID_BUTTON_GETPING, NetPlay::OnGUIEvent)
EVT_BUTTON(ID_BUTTON_CHAT, NetPlay::OnGUIEvent)
EVT_TEXT_ENTER(ID_CHAT, NetPlay::OnGUIEvent)
EVT_BUTTON(ID_BUTTON_QUIT, NetPlay::OnDisconnect)
EVT_CLOSE(NetPlay::OnQuit)
BEGIN_EVENT_TABLE(NetPlayDiag, wxFrame)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, NetPlayDiag::OnThread)
END_EVENT_TABLE()
NetPlay::NetPlay(wxWindow* parent, std::string GamePaths, std::string GameNames) :
wxFrame(parent, wxID_ANY, _T("Net Play"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~ wxMAXIMIZE_BOX & ~ wxCLOSE_BOX)
NetPlay* netplay_ptr = NULL;
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';
m_games = GameNames; m_paths = GamePaths;
m_isHosting = 2; m_ready = m_clients_ready = false;
m_loopframe = m_frame = m_NetModel = m_numClients = 0;
//PanicAlert("ALERT: NetPlay is not 100%% functional !!!!");
DrawGUI();
}
wxPanel* const panel = new wxPanel(this);
NetPlay::~NetPlay()
{
ConfigIni.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
// top row
wxStaticText* const nick_lbl = new wxStaticText(panel, wxID_ANY, wxT("Nickname :"), wxDefaultPosition, wxDefaultSize);
m_nickname_text = new wxTextCtrl(panel, wxID_ANY, wxT("Player"));
ConfigIni.Set("Netplay", "Nickname", m_nick);
ConfigIni.Set("Netplay", "UsedPort", (int)m_port);
ConfigIni.Set("Netplay", "LastIP", m_address);
wxBoxSizer* const nick_szr = new wxBoxSizer(wxHORIZONTAL);
nick_szr->Add(nick_lbl, 0, wxCENTER);
nick_szr->Add(m_nickname_text, 0, wxALL, 5);
ConfigIni.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
}
void NetPlay::OnJoin(wxCommandEvent& WXUNUSED(event))
{
unsigned short server_port;
// tabs
wxNotebook* const notebook = new wxNotebook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
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(':'));
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)
// connect tab
{
// Try to Bind the UDP Socket
if (sock_client_UDP.Bind(m_port))
{
m_sock_client = new ClientSide(this, sock_client, sock_client_UDP, m_address, m_nick);
m_sock_client->Create();
m_sock_client->Run();
wxStaticText* const ip_lbl = new wxStaticText(connect_tab, wxID_ANY, wxT("Address :"), wxDefaultPosition, wxDefaultSize);
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_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, wxT("2626"));
// Create the GUI
m_isHosting = false;
DrawNetWindow();
}
else
{
SetTitle(wxT("Net Play"));
PanicAlert("Can't Bind UDP socket on the specified Port: %d ! \n"
"Make sure port is forwarded and not in use !", m_port);
}
wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, wxT("Connect"));
//connect_button->Disable();
_connect_macro_(connect_btn, NetPlaySetupDiag::OnJoin, wxEVT_COMMAND_BUTTON_CLICKED, this);
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")
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]")
wxT("\n\nAll players should try to use the same Dolphin version and settings.")
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"));
PanicAlert("Can't connect to the specified IP Address ! \nMake sure Hosting port is forwarded !");
}
}
wxStaticText* const port_lbl = new wxStaticText(host_tab, wxID_ANY, wxT("Port :"), wxDefaultPosition, wxDefaultSize);
m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, wxT("2626"));
void NetPlay::OnHost(wxCommandEvent& WXUNUSED(event))
{
TryParseInt(m_SetPort->GetValue().mb_str(), (int*)&m_port);
wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, wxT("Host"));
//host_button->Disable();
_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) {
PanicAlert("No Game Selected ! Please select a Game...");
return;
}
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);
wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT, 5);
top_szr->Add(m_host_port_text, 0);
m_NetModel = m_NetMode->GetSelection();
m_selectedGame = std::string(m_GameList_str[m_GameList->GetSelection()].mb_str());
NOTICE_LOG(NETPLAY,"Game has been set to : %s \n",m_selectedGame.c_str());
wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL);
host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
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
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));
host_tab->SetSizerAndFit(host_szr);
}
netmodes_str.Add(wxT("P2P Versus (2 players, faster)"));
// TODO : netmodes_str.Add(wxT("Server Mode (4 players, slower)"));
// bottom row
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
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"));
panel->SetSizerAndFit(main_szr);
// Tow window, Nickname & Port settings
m_SetNick_text = new wxStaticText(panel, wxID_ANY, wxT(" Nickname : "), wxDefaultPosition, wxDefaultSize);
m_SetNick = new wxTextCtrl(panel, ID_SETNICK, wxT("LOLWUT!"), wxDefaultPosition, wxDefaultSize);
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);
//wxBoxSizer* const diag_szr = new wxBoxSizer(wxVERTICAL);
//diag_szr->Add(panel);
//SetSizerAndFit(diag_szr);
// Sizers CONNECT
wxBoxSizer* sConnectTop = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* sConnectSizer = new wxBoxSizer(wxVERTICAL);
sConnectTop->Add(m_ConAddr_text, 0, wxALL|wxALIGN_CENTER, 5);
sConnectTop->Add(m_ConAddr, 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);
main_szr->SetSizeHints(this);
Center();
Show();
}
// String of the type : FPSxPINGxFRAME_DELAY
void NetPlay::UpdateNetWindow(bool update_infos, wxString infos)
void NetPlaySetupDiag::OnHost(wxCommandEvent& event)
{
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
(wxString::FromAscii(StringFromFormat(" Fps : %s | Ping : %s | Frame Delay : %s",
str_arr[0].c_str(), str_arr[1].c_str(),
str_arr[2].c_str()).c_str()) );
if (-1 == m_game_lbox->GetSelection())
{
PanicAlert("You must choose a game!!");
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
{
m_critical.Enter();
//m_Game_str->SetLabel(wxString::Format(wxT(" Game : %s"), m_selectedGame.c_str()));
m_Game_str->SetLabel(wxString::FromAscii(std::string("Game " + m_selectedGame).c_str()));
m_critical.Leave();
PanicAlert("Failed to Listen!!");
npd->Destroy();
// dialog will delete netplay
//delete ::netplay_ptr;
}
}
void NetPlay::OnGUIEvent(wxCommandEvent& event)
void NetPlaySetupDiag::OnJoin(wxCommandEvent& event)
{
unsigned char value;;
switch (event.GetId())
// warning removal
event.GetId();
if (::netplay_ptr)
{
case ID_READY:
{
std::string buffer;
value = 0x40;
if (!m_ready)
buffer = ">> "+m_nick+" is now ready !\n";
else
buffer = ">> "+m_nick+" is now Unready !\n";
m_ready = !m_ready;
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;
}
PanicAlert("A NetPlay window is already open!!");
return;
}
}
// GameList popup window
BEGIN_EVENT_TABLE(GameListPopup, wxDialog)
EVT_BUTTON(wxID_OK, GameListPopup::OnButtons)
EVT_BUTTON(wxID_CANCEL, GameListPopup::OnButtons)
END_EVENT_TABLE()
GameListPopup::GameListPopup(NetPlay *parent, wxArrayString GameNames) :
wxDialog(parent, wxID_ANY, _T("Choose a Game :"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
m_netParent = parent;
m_GameList_str = GameNames;
m_GameList = new wxListBox(this, ID_GAMELIST, wxDefaultPosition, wxSize(300, 250),
GameNames, wxLB_SINGLE | wxLB_SORT | wxLB_NEEDED_SB);
m_Cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize);
m_Accept = new wxButton(this, wxID_OK, wxT("Apply"), wxDefaultPosition, wxDefaultSize);
wxBoxSizer* sButtons = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL);
sButtons->Add(m_Cancel, 0, wxALL, 0);
sButtons->AddStretchSpacer(1);
sButtons->Add(m_Accept, 0, wxALL | wxALIGN_RIGHT, 0);
sMain->Add(m_GameList, 0, wxALL | wxEXPAND, 2);
sMain->Add(sButtons, 0, wxALL | wxEXPAND, 5);
SetSizerAndFit(sMain);
Center(); Layout(); Show();
}
void GameListPopup::OnButtons(wxCommandEvent& event)
{
switch (event.GetId())
NetPlayDiag* const npd = new NetPlayDiag(m_parent, m_game_list, "");
unsigned long port = 0;
m_connect_port_text->GetValue().ToULong(&port);
::netplay_ptr = new NetPlayClient(std::string(m_connect_ip_text->GetValue().mb_str())
, (u16)port, std::string(m_nickname_text->GetValue().mb_str()), npd);
if (::netplay_ptr->is_connected)
{
case wxID_OK:
if (m_GameList->GetSelection() != wxNOT_FOUND)
m_netParent->ChangeSelectedGame(std::string(m_GameList_str[m_GameList->GetSelection()].mb_str()));
//NetPlayServerDiag* const npsd =
npd->Show();
Destroy();
break;
case wxID_CANCEL:
Destroy();
break;
}
else
{
//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_
#define _NETWINDOW_H_
#include "CommonTypes.h"
#include <queue>
#include <string>
#include <wx/wx.h>
#include <wx/sizer.h>
#include <wx/dialog.h>
#include <wx/notebook.h>
#include <wx/gbsizer.h>
#include <wx/listbox.h>
//#include <wx/thread.h>
#include <wx/thread.h>
#include "GameListCtrl.h"
#include "Globals.h"
#include "BootManager.h"
#include "Common.h"
#include "NetStructs.h"
#include "Core.h"
#include "pluginspecs_pad.h"
#include "HW/SI.h"
#include "HW/SI_Device.h"
#include "HW/SI_DeviceGCController.h"
#include "Timer.h"
// just leaving these here so i can find something later if i need it
//#include "Frame.h"
//#include "Globals.h"
//#include "BootManager.h"
//#include "Common.h"
//#include "NetStructs.h"
//#include "Core.h"
//#include "pluginspecs_pad.h"
//#include "HW/SI.h"
//#include "HW/SI_Device.h"
//#include "HW/SI_DeviceGCController.h"
//#include "Timer.h"
#ifdef _DEBUG
#define NET_DEBUG
#endif
#include "LockingQueue.h"
// Use TCP instead of UDP to send pad data @ 60fps. Suitable and better for LAN netplay,
// Unrealistic for Internet netplay, unless you have an uberfast connexion (<10ms ping)
// #define USE_TCP
class ServerSide;
class ClientSide;
class NetPlay : public wxFrame
class NetPlaySetupDiag : public wxFrame
{
public:
NetPlay(wxWindow* parent, std::string GamePath = "", std::string GameName = "");
~NetPlay();
public:
NetPlaySetupDiag(wxWindow* parent, const CGameListCtrl* const game_list);
void UpdateNetWindow(bool update_infos, wxString=wxT("NULL"));
void AppendText(const wxString text) { m_Logging->AppendText(text); }
private:
void OnJoin(wxCommandEvent& event);
void OnHost(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
// Send and receive pads values
bool GetNetPads(u8 pad_nb, SPADStatus, u32 *netvalues);
void ChangeSelectedGame(std::string game);
void IsGameFound(unsigned char*, std::string);
std::string GetSelectedGame() { wxCriticalSectionLocker lock(m_critical); return m_selectedGame; }
wxTextCtrl *m_nickname_text,
*m_host_port_text,
*m_connect_port_text,
*m_connect_ip_text;
void LoadGame();
wxListBox* m_game_lbox;
protected:
// Protects our vars from being fuxored by threads
wxCriticalSection m_critical;
// this draws the GUI, ya rly
void DrawGUI();
void DrawNetWindow();
// event handlers
void OnGUIEvent(wxCommandEvent& event);
void OnDisconnect(wxCommandEvent& event);
void OnNetEvent(wxCommandEvent& event);
void OnQuit(wxCloseEvent& event);
void OnJoin(wxCommandEvent& event);
void OnHost(wxCommandEvent& event);
// 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()
const CGameListCtrl* const m_game_list;
};
class GameListPopup : public wxDialog
class NetPlayDiag : public wxFrame
{
public:
GameListPopup(NetPlay *net_ptr, wxArrayString GameNames);
~GameListPopup() {}
protected:
void OnButtons(wxCommandEvent& event);
wxArrayString m_GameList_str;
NetPlay* m_netParent;
wxListBox *m_GameList;
wxButton *m_Accept;
wxButton *m_Cancel;
DECLARE_EVENT_TABLE()
public:
NetPlayDiag(wxWindow* parent, const CGameListCtrl* const game_list
, const std::string& game, const bool is_hosting = false);
~NetPlayDiag();
LockingQueue<std::string> chat_msgs;
//std::string chat_msg;
void OnStart(wxCommandEvent& event);
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
{
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
};
DECLARE_EVENT_TYPE(wxEVT_THREAD, -1)
#endif // _NETWINDOW_H_

View File

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