dolphin/Source/Core/DolphinWX/Src/NetSockets.cpp

607 lines
15 KiB
C++

// 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 1
SendEvent(ADD_TEXT, std::string(text.mb_str()));
#else
wxMutexGuiEnter();
m_netptr->AppendText(text);
wxMutexGuiLeave();
#endif
}
void NetEvent::SendEvent(int EventType, const std::string text, int integer)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, wxID_ANY);
event.SetId( EventType );
event.SetInt( integer );
event.SetString( wxString::FromAscii(text.c_str()) );
m_netptr->AddPendingEvent(event);
}
//--------------------------------
// SERVER SIDE THREAD
//--------------------------------
ServerSide::ServerSide(NetPlay* netptr, sf::SocketTCP socket, 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::Format(wxT("*Player : %s left the game.\n\n"),
player_left.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((wxT("*Connection established to %s (%s:%d)\n"),
m_client[socketnb].nick.c_str(), Address.ToString().c_str(), m_client[m_numplayers].port)) );
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::Format( wxT("*Connection established to %s (%s)\n*Game is : %s\n "),
wxString(m_hostnick.c_str(),wxConvUTF8 ) , wxString(m_addr.c_str(),wxConvUTF8 ) , wxString(m_selectedgame.c_str() , wxConvUTF8) ));
}
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(_("WARNING : You do not have the Selected Game !\n"));
NOTICE_LOG(NETPLAY,"Game '%s' not found!",m_selectedgame);
}
}
}
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);
}