From ef10602d5cf4952f73a3d7e3ea57f4a3477a9376 Mon Sep 17 00:00:00 2001 From: lotodore Date: Tue, 20 Mar 2007 02:20:50 +0000 Subject: [PATCH] Initial handshake and network game start. Works with multiple clients. Not yet stable, because it is not thread-safe. GUI callbacks not complete yet (server dialog not updated). --- pokerth.pro | 3 +- src/gui/guiinterface.h | 3 +- .../connecttoserverdialogimpl.cpp | 4 +- src/gui/qt/guiwrapper.cpp | 9 +- src/gui/qt/guiwrapper.h | 8 +- src/gui/qt/mainwindow/mainwindowimpl.cpp | 39 ++--- src/gui/qt/mainwindow/mainwindowimpl.h | 7 +- .../waitforservertostartgamedialogimpl.cpp | 4 + .../waitforservertostartgamedialogimpl.h | 1 + src/net/clientcallback.h | 8 +- src/net/clientstate.h | 20 ++- src/net/clientthread.h | 3 +- src/net/common/clientstate.cpp | 49 +++++- src/net/common/clientthread.cpp | 12 +- src/net/common/connectdata.cpp | 9 +- src/net/common/netpacket.cpp | 162 ++++++++++++++++-- src/net/common/receiverhelper.cpp | 30 ++-- .../{netcallback.cpp => servercallback.cpp} | 4 +- src/net/common/serverrecvstate.cpp | 71 +++++++- src/net/common/serverrecvthread.cpp | 110 ++++++++++++ src/net/common/serverthread.cpp | 26 ++- src/net/common/sessiondata.cpp | 30 ++++ src/net/connectdata.h | 7 +- src/net/netpacket.h | 82 ++++++++- src/net/receiverhelper.h | 1 + src/net/{netcallback.h => servercallback.h} | 14 +- src/net/serverrecvstate.h | 26 ++- src/net/serverrecvthread.h | 18 +- src/net/serverthread.h | 8 +- src/net/sessiondata.h | 56 ++++++ src/net/socket_msg.h | 8 +- src/session.cpp | 11 +- src/session.h | 1 + 33 files changed, 731 insertions(+), 113 deletions(-) rename src/net/common/{netcallback.cpp => servercallback.cpp} (93%) create mode 100644 src/net/common/sessiondata.cpp rename src/net/{netcallback.h => servercallback.h} (82%) create mode 100644 src/net/sessiondata.h diff --git a/pokerth.pro b/pokerth.pro index d5797376..2a8d8bb3 100755 --- a/pokerth.pro +++ b/pokerth.pro @@ -145,9 +145,10 @@ SOURCES += src/game.cpp \ src/net/common/serverexception.cpp \ src/net/common/serverrecvthread.cpp \ src/net/common/serverrecvstate.cpp \ + src/net/common/servercallback.cpp \ + src/net/common/sessiondata.cpp \ src/net/common/socket_helper_cmn.cpp \ src/net/common/clientexception.cpp \ - src/net/common/netcallback.cpp \ src/net/common/netcontext.cpp \ src/net/common/netexception.cpp \ src/net/common/receiverhelper.cpp \ diff --git a/src/gui/guiinterface.h b/src/gui/guiinterface.h index 9c5d3c55..15bfda9d 100644 --- a/src/gui/guiinterface.h +++ b/src/gui/guiinterface.h @@ -21,6 +21,7 @@ #define GUIINTERFACE_H #include +#include #include class Game; @@ -28,7 +29,7 @@ class Session; class HandInterface; -class GuiInterface : public ClientCallback { +class GuiInterface : public ClientCallback, public ServerCallback { public: virtual ~GuiInterface(); diff --git a/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp b/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp index 822622c3..2af08169 100644 --- a/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp +++ b/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp @@ -44,9 +44,9 @@ void connectToServerDialogImpl::refresh(int actionID) { default: { label_actionMessage->setText("Please wait..."); } } - progressBar->setValue(actionID*(100/MSG_SOCK_LAST)); + progressBar->setValue(actionID*(100/MSG_SOCK_LIMIT_CONNECT)); - if (actionID == MSG_SOCK_LAST) + if (actionID == MSG_SOCK_LIMIT_CONNECT) QTimer::singleShot(1000, this, SLOT(accept())); } diff --git a/src/gui/qt/guiwrapper.cpp b/src/gui/qt/guiwrapper.cpp index 0928a895..fcc338ba 100644 --- a/src/gui/qt/guiwrapper.cpp +++ b/src/gui/qt/guiwrapper.cpp @@ -88,5 +88,10 @@ void GuiWrapper::meInAction() const { myW->meInAction(); } void GuiWrapper::logPlayerActionMsg(string playerName, int action, int setValue) { myLog->logPlayerActionMsg(playerName, action, setValue); } void GuiWrapper::logNewGameHandMsg(int gameID, int handID) { myLog->logNewGameHandMsg(gameID, handID); } -void GuiWrapper::SignalNetSuccess(int actionID) { myW->SignalNetSuccess(actionID); } -void GuiWrapper::SignalNetError(int errorID, int osErrorID) { myW->SignalNetError(errorID, osErrorID); } +void GuiWrapper::SignalNetClientConnect(int actionID) { myW->SignalNetClientConnect(actionID); } +void GuiWrapper::SignalNetClientGameInfo(int actionID) { myW->SignalNetClientGameInfo(actionID); } +void GuiWrapper::SignalNetClientError(int errorID, int osErrorID) { myW->SignalNetClientError(errorID, osErrorID); } + +void GuiWrapper::SignalNetServerSuccess(int actionID) { } +void GuiWrapper::SignalNetServerError(int errorID, int osErrorID) { } + diff --git a/src/gui/qt/guiwrapper.h b/src/gui/qt/guiwrapper.h index 434097c8..89db6da9 100644 --- a/src/gui/qt/guiwrapper.h +++ b/src/gui/qt/guiwrapper.h @@ -88,8 +88,12 @@ public: void logPlayerActionMsg(std::string playerName, int action, int setValue) ; void logNewGameHandMsg(int gameID, int handID) ; - void SignalNetSuccess(int actionID); - void SignalNetError(int errorID, int osErrorID); + void SignalNetClientConnect(int actionID); + void SignalNetClientGameInfo(int actionID); + void SignalNetClientError(int errorID, int osErrorID); + + void SignalNetServerSuccess(int actionID); + void SignalNetServerError(int errorID, int osErrorID); private: diff --git a/src/gui/qt/mainwindow/mainwindowimpl.cpp b/src/gui/qt/mainwindow/mainwindowimpl.cpp index f7e3eb70..2394eab7 100755 --- a/src/gui/qt/mainwindow/mainwindowimpl.cpp +++ b/src/gui/qt/mainwindow/mainwindowimpl.cpp @@ -26,7 +26,7 @@ #include "connecttoserverdialogimpl.h" #include "createnetworkgamedialogimpl.h" #include "startnetworkgamedialogimpl.h" - +#include "waitforservertostartgamedialogimpl.h" #include "startsplash.h" #include "mycardspixmaplabel.h" @@ -427,6 +427,7 @@ mainWindowImpl::mainWindowImpl(QMainWindow *parent) myConnectToServerDialog = new connectToServerDialogImpl(this); myStartNetworkGameDialog = new startNetworkGameDialogImpl(this); myCreateNetworkGameDialog = new createNetworkGameDialogImpl(this); + myWaitingForServerGameDialog = new waitForServerToStartGameDialogImpl(this); //Connects connect(dealFlopCards0Timer, SIGNAL(timeout()), this, SLOT( dealFlopCards1() )); @@ -481,8 +482,10 @@ mainWindowImpl::mainWindowImpl(QMainWindow *parent) connect ( pushButton_break, SIGNAL( clicked()), this, SLOT ( breakButtonClicked() ) ); // auch wieder starten!!!! //Nachrichten Thread-Save - connect(this, SIGNAL(SignalNetSuccess(int)), myConnectToServerDialog, SLOT(refresh(int))); - connect(this, SIGNAL(SignalNetError(int, int)), myConnectToServerDialog, SLOT(error(int, int))); + connect(this, SIGNAL(SignalNetClientConnect(int)), myConnectToServerDialog, SLOT(refresh(int))); + connect(this, SIGNAL(SignalNetClientGameInfo(int)), myWaitingForServerGameDialog, SLOT(refresh(int))); + // TODO Fix, errors MUST be global, not within one dialog. + connect(this, SIGNAL(SignalNetClientError(int, int)), myConnectToServerDialog, SLOT(error(int, int))); // textBrowser_Log->append(QString::number(this->pos().x(),10)+" "+QString::number(this->pos().y(),10)); // textBrowser_Log->append(QString::number(this->x(),10)+" "+QString::number(this->y(),10)); @@ -586,29 +589,18 @@ void mainWindowImpl::callAboutPokerthDialog() { void mainWindowImpl::callCreateNetworkGameDialog() { - myCreateNetworkGameDialog->showDialog(); + myCreateNetworkGameDialog->exec(); // if (myCreateNetworkGameDialog->result() == QDialog::Accepted ) { + mySession->terminateNetworkServer(); mySession->startNetworkServer(); -// -// mySession->terminateNetworkClient(); -// -// // Maybe use QUrl::toPunycode. -// mySession->startNetworkClient( -// myJoinNetworkGameDialog->lineEdit_ipAddress->text().toUtf8().constData(), -// myJoinNetworkGameDialog->spinBox_port->value(), -// myJoinNetworkGameDialog->checkBox_ipv6->isChecked(), -// myJoinNetworkGameDialog->lineEdit_password->text().toUtf8().constData()); -// -// //Dialog mit Statusbalken -// myConnectToServerDialog->exec(); -// -// if (myConnectToServerDialog->result() == QDialog::Rejected ) { -// mySession->terminateNetworkClient(); -// actionJoin_network_Game->trigger(); // re-trigger -// } -// + + myStartNetworkGameDialog->exec(); + + if (myStartNetworkGameDialog->result() == QDialog::Accepted ) { + mySession->initiateNetworkServerGame(); + } } } @@ -635,6 +627,9 @@ void mainWindowImpl::callJoinNetworkGameDialog() { mySession->terminateNetworkClient(); actionJoin_network_Game->trigger(); // re-trigger } + else { + myWaitingForServerGameDialog->exec(); + } } diff --git a/src/gui/qt/mainwindow/mainwindowimpl.h b/src/gui/qt/mainwindow/mainwindowimpl.h index f003b182..28a45ca8 100755 --- a/src/gui/qt/mainwindow/mainwindowimpl.h +++ b/src/gui/qt/mainwindow/mainwindowimpl.h @@ -42,6 +42,7 @@ class joinNetworkGameDialogImpl; class connectToServerDialogImpl; class createNetworkGameDialogImpl; class startNetworkGameDialogImpl; +class waitForServerToStartGameDialogImpl; class QColor; @@ -88,8 +89,9 @@ public: void setSpeeds(); signals: - void SignalNetSuccess(int actionID); - void SignalNetError(int errorID, int osErrorID); + void SignalNetClientConnect(int actionID); + void SignalNetClientGameInfo(int actionID); + void SignalNetClientError(int errorID, int osErrorID); public slots: @@ -258,6 +260,7 @@ private: connectToServerDialogImpl *myConnectToServerDialog; startNetworkGameDialogImpl *myStartNetworkGameDialog; createNetworkGameDialogImpl *myCreateNetworkGameDialog; + waitForServerToStartGameDialogImpl *myWaitingForServerGameDialog; int maxQuantityPlayers; diff --git a/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.cpp b/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.cpp index 05b1dc46..391fed1a 100644 --- a/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.cpp +++ b/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.cpp @@ -31,6 +31,10 @@ waitForServerToStartGameDialogImpl::waitForServerToStartGameDialogImpl(QWidget * } +void waitForServerToStartGameDialogImpl::refresh(int actionID) { + QTimer::singleShot(1000, this, SLOT(accept())); +} + void waitForServerToStartGameDialogImpl::cancel() { } diff --git a/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.h b/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.h index 376463d7..f0ba68c7 100644 --- a/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.h +++ b/src/gui/qt/waitforservertostartgamedialog/waitforservertostartgamedialogimpl.h @@ -35,6 +35,7 @@ public: public slots: + void refresh(int actionID); void cancel(); void keyPressEvent ( QKeyEvent * event ); }; diff --git a/src/net/clientcallback.h b/src/net/clientcallback.h index 8e75fe82..4383d063 100644 --- a/src/net/clientcallback.h +++ b/src/net/clientcallback.h @@ -21,12 +21,14 @@ #ifndef _CLIENTCALLBACK_H_ #define _CLIENTCALLBACK_H_ -#include - -class ClientCallback : public NetCallback +class ClientCallback { public: virtual ~ClientCallback(); + + virtual void SignalNetClientConnect(int actionID) = 0; + virtual void SignalNetClientGameInfo(int actionID) = 0; + virtual void SignalNetClientError(int errorID, int osErrorID) = 0; }; #endif diff --git a/src/net/clientstate.h b/src/net/clientstate.h index 4c84809a..ee7dbdb8 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -164,7 +164,7 @@ public: virtual ~ClientStateWaitSession(); - // sleep. + // select on socket. virtual int Process(ClientThread &client); protected: @@ -173,6 +173,24 @@ protected: ClientStateWaitSession(); }; +// State: Wait for start of the game or start info. +class ClientStateWaitGame : public ClientState +{ +public: + // Access the state singleton. + static ClientStateWaitGame &Instance(); + + virtual ~ClientStateWaitGame(); + + // select on socket. + virtual int Process(ClientThread &client); + +protected: + + // Protected constructor - this is a singleton. + ClientStateWaitGame(); +}; + // State: Final (TODO). class ClientStateFinal : public ClientState { diff --git a/src/net/clientthread.h b/src/net/clientthread.h index b53c38d5..4f72e7a5 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -24,10 +24,10 @@ #include #include #include +#include class ClientContext; class ClientState; -class ClientCallback; class SenderThread; class ReceiverHelper; class ClientSenderCallback; @@ -77,6 +77,7 @@ friend class ClientStateStartConnect; friend class ClientStateConnecting; friend class ClientStateStartSession; friend class ClientStateWaitSession; +friend class ClientStateWaitGame; }; #endif diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 401460a2..cd0dad33 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -321,7 +321,7 @@ ClientStateStartSession::~ClientStateStartSession() int ClientStateStartSession::Process(ClientThread &client) { - boost::shared_ptr packet(new TestNetPacket(10)); + boost::shared_ptr packet(new NetPacketInit(10)); client.GetSender().Send(packet, client.GetContext().GetSocket()); client.SetState(ClientStateWaitSession::Instance()); @@ -356,15 +356,54 @@ ClientStateWaitSession::Process(ClientThread &client) boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - if (tmpPacket.get()) + if (tmpPacket.get() && tmpPacket->ToNetPacketInitAck()) { - client.SetState(ClientStateFinal::Instance()); + client.SetState(ClientStateWaitGame::Instance()); retVal = MSG_SOCK_SESSION_DONE; } - else + else // TODO: handle error packet + { + retVal = MSG_SOCK_INTERNAL_PENDING; + } + + return retVal; +} + +//----------------------------------------------------------------------------- + +ClientStateWaitGame & +ClientStateWaitGame::Instance() +{ + static ClientStateWaitGame state; + return state; +} + +ClientStateWaitGame::ClientStateWaitGame() +{ +} + +ClientStateWaitGame::~ClientStateWaitGame() +{ +} + +int +ClientStateWaitGame::Process(ClientThread &client) +{ + int retVal; + ClientContext &context = client.GetContext(); + + // delegate to receiver helper class + + boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); + + if (tmpPacket.get() && tmpPacket->ToNetPacketGameStart()) + { + client.SetState(ClientStateFinal::Instance()); + retVal = MSG_SOCK_GAME_START; + } + else // TODO: handle error packet { retVal = MSG_SOCK_INTERNAL_PENDING; - Thread::Msleep(CLIENT_WAIT_TIMEOUT_MSEC); } return retVal; diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 326e1b2f..4cce357f 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -43,7 +42,7 @@ public: // For now, we ignore the socket. // Just signal the error. // We assume that the client thread will be terminated. - m_client.GetCallback().SignalNetError(errorID, osErrorID); + m_client.GetCallback().SignalNetClientError(errorID, osErrorID); } private: @@ -97,11 +96,16 @@ ClientThread::Main() { int msg = GetState().Process(*this); if (msg != MSG_SOCK_INTERNAL_PENDING) - GetCallback().SignalNetSuccess(msg); + { + if (msg <= MSG_SOCK_LIMIT_CONNECT) + GetCallback().SignalNetClientConnect(msg); + else + GetCallback().SignalNetClientGameInfo(msg); + } } } catch (const NetException &e) { - GetCallback().SignalNetError(e.GetErrorId(), e.GetOsErrorCode()); + GetCallback().SignalNetClientError(e.GetErrorId(), e.GetOsErrorCode()); } GetSender().SignalTermination(); GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT); diff --git a/src/net/common/connectdata.cpp b/src/net/common/connectdata.cpp index 5ed53ab1..1cc98117 100644 --- a/src/net/common/connectdata.cpp +++ b/src/net/common/connectdata.cpp @@ -22,7 +22,6 @@ ConnectData::ConnectData() : m_sockfd(INVALID_SOCKET) { - bzero(&m_sockaddr, sizeof(m_sockaddr)); } ConnectData::~ConnectData() @@ -31,3 +30,11 @@ ConnectData::~ConnectData() CLOSESOCKET(m_sockfd); } +SOCKET +ConnectData::ReleaseSocket() +{ + SOCKET tmpSock = m_sockfd; + m_sockfd = INVALID_SOCKET; + return tmpSock; +} + diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index 807d7097..3d2016a2 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -25,35 +25,61 @@ NetPacket::~NetPacket() { } -//----------------------------------------------------------------------------- - -TestNetPacket::TestNetPacket() +const NetPacketInit * +NetPacket::ToNetPacketInit() const { + return NULL; } -TestNetPacket::TestNetPacket(u_int32_t value) +const NetPacketInitAck * +NetPacket::ToNetPacketInitAck() const { - m_data.head.type = htons(NET_TYPE_TEST); - m_data.head.length = htons(sizeof(m_data)); + return NULL; +} + +const NetPacketGameStart * +NetPacket::ToNetPacketGameStart() const +{ + return NULL; +} + +//----------------------------------------------------------------------------- + +NetPacketInit::NetPacketInit() +{ + Init(); +} + +NetPacketInit::NetPacketInit(u_int32_t value) +{ + Init(); m_data.test = htonl(value); } -TestNetPacket::~TestNetPacket() +NetPacketInit::~NetPacketInit() { } -NetPacketHeader * -TestNetPacket::GetData() -{ - return (NetPacketHeader *)&m_data; -} - void -TestNetPacket::SetData(const NetPacketHeader *p) +NetPacketInit::Init() +{ + m_data.head.type = htons(NET_TYPE_INIT); + m_data.head.length = htons(sizeof(m_data)); + m_data.test = htonl(0); +} + +const NetPacketHeader * +NetPacketInit::GetData() const +{ + return (const NetPacketHeader *)&m_data; +} + +void +NetPacketInit::SetData(const NetPacketHeader *p) { u_int16_t tmpLen = ntohs(p->length); if (tmpLen != sizeof(m_data) - || ntohs(p->type) != NET_TYPE_TEST) + || ntohs(p->type) != NET_TYPE_INIT) { throw NetException(ERR_SOCK_INTERNAL, 0); } @@ -61,3 +87,109 @@ TestNetPacket::SetData(const NetPacketHeader *p) memcpy(&m_data, p, tmpLen); } +const NetPacketInit * +NetPacketInit::ToNetPacketInit() const +{ + return this; +} + +//----------------------------------------------------------------------------- + +NetPacketInitAck::NetPacketInitAck() +{ + Init(); +} + +NetPacketInitAck::NetPacketInitAck(u_int32_t value) +{ + Init(); + m_data.test = htonl(value); +} + +NetPacketInitAck::~NetPacketInitAck() +{ +} + +void +NetPacketInitAck::Init() +{ + m_data.head.type = htons(NET_TYPE_INIT_ACK); + m_data.head.length = htons(sizeof(m_data)); + m_data.test = htonl(0); +} + +const NetPacketHeader * +NetPacketInitAck::GetData() const +{ + return (const NetPacketHeader *)&m_data; +} + +void +NetPacketInitAck::SetData(const NetPacketHeader *p) +{ + u_int16_t tmpLen = ntohs(p->length); + if (tmpLen != sizeof(m_data) + || ntohs(p->type) != NET_TYPE_INIT_ACK) + { + throw NetException(ERR_SOCK_INTERNAL, 0); + } + + memcpy(&m_data, p, tmpLen); +} + +const NetPacketInitAck * +NetPacketInitAck::ToNetPacketInitAck() const +{ + return this; +} + +//----------------------------------------------------------------------------- + +NetPacketGameStart::NetPacketGameStart() +{ + Init(); +} + +NetPacketGameStart::NetPacketGameStart(u_int32_t value) +{ + Init(); + m_data.test = htonl(value); +} + +NetPacketGameStart::~NetPacketGameStart() +{ +} + +void +NetPacketGameStart::Init() +{ + m_data.head.type = htons(NET_TYPE_GAME_START); + m_data.head.length = htons(sizeof(m_data)); + m_data.test = htonl(0); +} + +const NetPacketHeader * +NetPacketGameStart::GetData() const +{ + return (NetPacketHeader *)&m_data; +} + +void +NetPacketGameStart::SetData(const NetPacketHeader *p) +{ + u_int16_t tmpLen = ntohs(p->length); + if (tmpLen != sizeof(m_data) + || ntohs(p->type) != NET_TYPE_GAME_START) + { + throw NetException(ERR_SOCK_INTERNAL, 0); + } + + memcpy(&m_data, p, tmpLen); +} + +const NetPacketGameStart * +NetPacketGameStart::ToNetPacketGameStart() const +{ + return this; +} + diff --git a/src/net/common/receiverhelper.cpp b/src/net/common/receiverhelper.cpp index 228651dc..d9fd0d91 100644 --- a/src/net/common/receiverhelper.cpp +++ b/src/net/common/receiverhelper.cpp @@ -24,7 +24,6 @@ using namespace std; -#define RECV_TIMEOUT_MSEC 50 ReceiverHelper::ReceiverHelper() : m_socket(INVALID_SOCKET), m_tmpInBufSize(0) @@ -124,18 +123,25 @@ ReceiverHelper::InternalCreateNetPacket(const NetPacketHeader *p) { boost::shared_ptr tmpPacket; - switch(ntohs(p->type)) + try { - case NET_TYPE_TEST: - try - { - tmpPacket = boost::shared_ptr(new TestNetPacket); - tmpPacket->SetData(p); - } catch (const NetException &) - { - tmpPacket.reset(); - } - break; + switch(ntohs(p->type)) + { + case NET_TYPE_INIT: + tmpPacket = boost::shared_ptr(new NetPacketInit); + break; + case NET_TYPE_INIT_ACK: + tmpPacket = boost::shared_ptr(new NetPacketInitAck); + break; + case NET_TYPE_GAME_START: + tmpPacket = boost::shared_ptr(new NetPacketGameStart); + break; + } + if (tmpPacket.get()) + tmpPacket->SetData(p); + } catch (const NetException &) + { + tmpPacket.reset(); } return tmpPacket; } diff --git a/src/net/common/netcallback.cpp b/src/net/common/servercallback.cpp similarity index 93% rename from src/net/common/netcallback.cpp rename to src/net/common/servercallback.cpp index b18b9198..fb1c0a65 100644 --- a/src/net/common/netcallback.cpp +++ b/src/net/common/servercallback.cpp @@ -17,10 +17,10 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include +#include -NetCallback::~NetCallback() +ServerCallback::~ServerCallback() { } diff --git a/src/net/common/serverrecvstate.cpp b/src/net/common/serverrecvstate.cpp index 1f38d47b..4def430c 100644 --- a/src/net/common/serverrecvstate.cpp +++ b/src/net/common/serverrecvstate.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include using namespace std; @@ -48,14 +51,78 @@ ServerRecvStateInit::~ServerRecvStateInit() } void -ServerRecvStateInit::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data) +ServerRecvStateInit::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr connData) { + boost::shared_ptr sessionData(new SessionData); + server.AddSession(connData, sessionData); } int ServerRecvStateInit::Process(ServerRecvThread &server) { - Thread::Msleep(SERVER_WAIT_TIMEOUT_MSEC); + SOCKET recvSock = server.Select(); + + if (recvSock != INVALID_SOCKET) + { + boost::shared_ptr packet = server.GetReceiver().Recv(recvSock); + boost::shared_ptr session = server.GetSession(recvSock); + + // Ignore if no session / no packet. + if (packet.get() && session.get()) + { + if (session->GetState() == SessionData::Init) + { + // Only accept init packets. + if (packet->ToNetPacketInit()) + { + boost::shared_ptr answer(new NetPacketInitAck); + server.GetSender().Send(answer, recvSock); + session->SetState(SessionData::Established); + } + else + { + // TODO send error message, invalid packet + } + } + else + { + // TODO send error message, invalid state + } + } + } + return MSG_SOCK_INIT_DONE; +} + +//----------------------------------------------------------------------------- + +ServerRecvStateStartGame & +ServerRecvStateStartGame::Instance() +{ + static ServerRecvStateStartGame state; + return state; +} + +ServerRecvStateStartGame::ServerRecvStateStartGame() +{ +} + +ServerRecvStateStartGame::~ServerRecvStateStartGame() +{ +} + +void +ServerRecvStateStartGame::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr connData) +{ + // TODO: send error msg +} + +int +ServerRecvStateStartGame::Process(ServerRecvThread &server) +{ + boost::shared_ptr answer(new NetPacketGameStart); + + server.SendToAllClients(answer); + return MSG_SOCK_INIT_DONE; } diff --git a/src/net/common/serverrecvthread.cpp b/src/net/common/serverrecvthread.cpp index 798e718d..8bad3b96 100644 --- a/src/net/common/serverrecvthread.cpp +++ b/src/net/common/serverrecvthread.cpp @@ -50,6 +50,28 @@ ServerRecvThread::~ServerRecvThread() { } +void +ServerRecvThread::StartGame() +{ + // TODO: not thread safe. use flag or something. + SetState(SERVER_START_GAME_STATE::Instance()); +} + +void +ServerRecvThread::SendToAllClients(boost::shared_ptr packet) +{ + // TODO: possible race condition if used in multithreading + SocketSessionMap::iterator i = m_sessions.begin(); + SocketSessionMap::iterator end = m_sessions.end(); + + while (i != end) + { + // TODO: desparately need clone here, this is very dangerous. + GetSender().Send(packet, i->first); + ++i; + } +} + void ServerRecvThread::AddConnection(boost::shared_ptr data) { @@ -91,6 +113,67 @@ ServerRecvThread::Main() } GetSender().SignalTermination(); GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT); + + // TODO: clear connection queue +} + +SOCKET +ServerRecvThread::Select() +{ + SOCKET retSock = INVALID_SOCKET; + + if (m_sessions.empty()) + { + Msleep(RECV_TIMEOUT_MSEC); // just sleep if there is no session + } + else + { + // wait for data + SOCKET maxSock = 0; + fd_set rdset; + FD_ZERO(&rdset); + + { + SocketSessionMap::iterator i = m_sessions.begin(); + SocketSessionMap::iterator end = m_sessions.end(); + + while (i != end) + { + SOCKET tmpSock = i->first; + FD_SET(tmpSock, &rdset); + if (tmpSock > maxSock) + maxSock = tmpSock; + ++i; + } + } + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = RECV_TIMEOUT_MSEC * 1000; + int selectResult = select(maxSock + 1, &rdset, NULL, NULL, &timeout); + if (!IS_VALID_SELECT(selectResult)) + { + throw ServerException(ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO()); + } + if (selectResult > 0) // one (or more) of the sockets is readable + { + // Check which socket is readable, return the first. + SocketSessionMap::iterator i = m_sessions.begin(); + SocketSessionMap::iterator end = m_sessions.end(); + + while (i != end) + { + SOCKET tmpSock = i->first; + if (FD_ISSET(tmpSock, &rdset)) + { + retSock = tmpSock; + break; + } + ++i; + } + } + } + return retSock; } ServerRecvState & @@ -106,6 +189,33 @@ ServerRecvThread::SetState(ServerRecvState &newState) m_curState = &newState; } +boost::shared_ptr +ServerRecvThread::GetSession(SOCKET sock) +{ + boost::shared_ptr tmpSession; + + SocketSessionMap::iterator pos = m_sessions.find(sock); + if (pos != m_sessions.end()) + { + tmpSession = pos->second; + } + return tmpSession; +} + +void +ServerRecvThread::AddSession(boost::shared_ptr connData, boost::shared_ptr sessionData) +{ + SocketSessionMap::iterator pos = m_sessions.lower_bound(connData->GetSocket()); + + // If pos points to a pair whose key is equivalent to the socket, this handle + // already exists within the list. + if (pos != m_sessions.end() && connData->GetSocket() == pos->first) + { + throw ServerException(ERR_SOCK_CONN_EXISTS, 0); + } + m_sessions.insert(pos, SocketSessionMap::value_type(connData->ReleaseSocket(), sessionData)); +} + SenderThread & ServerRecvThread::GetSender() { diff --git a/src/net/common/serverthread.cpp b/src/net/common/serverthread.cpp index 7badf16b..7c810587 100644 --- a/src/net/common/serverthread.cpp +++ b/src/net/common/serverthread.cpp @@ -29,7 +29,8 @@ #define NET_SERVER_LISTEN_BACKLOG 5 -ServerThread::ServerThread() +ServerThread::ServerThread(ServerCallback &cb) +: m_callback(cb) { m_context.reset(new ServerContext); } @@ -51,6 +52,22 @@ ServerThread::Init(unsigned serverPort, bool ipv6, const std::string &pwd) context.SetPassword(pwd); } +void +ServerThread::StartGame() +{ + if (!IsRunning()) + return; // TODO: throw exception + + // TODO: possible race condition + GetRecvThread().StartGame(); +} + +ServerCallback & +ServerThread::GetCallback() +{ + return m_callback; +} + void ServerThread::Main() { @@ -65,9 +82,9 @@ ServerThread::Main() // The main server thread is simple. It only accepts connections. AcceptLoop(); } - } catch (const NetException &) + } catch (const NetException &e) { - // TODO: callback. + GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode()); } } @@ -141,8 +158,7 @@ ServerThread::AcceptLoop() if (selectResult > 0) // accept is possible { boost::shared_ptr tmpData(new ConnectData); - socklen_t addrSize = sizeof(*tmpData->GetSockaddr()); - tmpData->SetSocket(accept(context.GetSocket(), (struct sockaddr *)tmpData->GetSockaddr(), &addrSize)); + tmpData->SetSocket(accept(context.GetSocket(), NULL, NULL)); if (!IS_VALID_SOCKET(tmpData->GetSocket())) { diff --git a/src/net/common/sessiondata.cpp b/src/net/common/sessiondata.cpp new file mode 100644 index 00000000..b0079f13 --- /dev/null +++ b/src/net/common/sessiondata.cpp @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2007 by Lothar May * + * * + * 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; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include + +SessionData::SessionData() +: m_id(SESSION_ID_INIT), m_state(SessionData::Init) +{ +} + +SessionData::~SessionData() +{ +} + diff --git a/src/net/connectdata.h b/src/net/connectdata.h index f1caee34..8cbe6fd1 100644 --- a/src/net/connectdata.h +++ b/src/net/connectdata.h @@ -33,14 +33,11 @@ public: {return m_sockfd;} void SetSocket(SOCKET sockfd) {m_sockfd = sockfd;} - const sockaddr_storage *GetSockaddr() const - {return &m_sockaddr;} - sockaddr_storage *GetSockaddr() - {return &m_sockaddr;} + + SOCKET ReleaseSocket(); private: SOCKET m_sockfd; - sockaddr_storage m_sockaddr; }; #endif diff --git a/src/net/netpacket.h b/src/net/netpacket.h index 98c702f2..17a6a800 100644 --- a/src/net/netpacket.h +++ b/src/net/netpacket.h @@ -26,7 +26,9 @@ #define MAX_PACKET_SIZE 256 -#define NET_TYPE_TEST 0 +#define NET_TYPE_INIT 0 +#define NET_TYPE_INIT_ACK 1 +#define NET_TYPE_GAME_START 2 #ifdef _MSC_VER #pragma pack(push, 2) @@ -40,7 +42,19 @@ struct NetPacketHeader u_int16_t length; }; -struct NetPacketInit +struct NetPacketInitData +{ + NetPacketHeader head; + u_int32_t test; +}; + +struct NetPacketInitAckData +{ + NetPacketHeader head; + u_int32_t test; +}; + +struct NetPacketGameStartData { NetPacketHeader head; u_int32_t test; @@ -52,6 +66,9 @@ struct NetPacketInit #pragma align 0 #endif +class NetPacketInit; +class NetPacketInitAck; +class NetPacketGameStart; class NetPacket { @@ -59,21 +76,68 @@ public: virtual ~NetPacket(); virtual void SetData(const NetPacketHeader *p) = 0; - virtual NetPacketHeader *GetData() = 0; + virtual const NetPacketHeader *GetData() const = 0; + + virtual const NetPacketInit *ToNetPacketInit() const; + virtual const NetPacketInitAck *ToNetPacketInitAck() const; + virtual const NetPacketGameStart *ToNetPacketGameStart() const; }; -class TestNetPacket : public NetPacket +class NetPacketInit : public NetPacket { public: - TestNetPacket(); - TestNetPacket(u_int32_t value); - virtual ~TestNetPacket(); + NetPacketInit(); + NetPacketInit(u_int32_t value); + virtual ~NetPacketInit(); - virtual NetPacketHeader *GetData(); + virtual const NetPacketHeader *GetData() const; virtual void SetData(const NetPacketHeader *p); + virtual const NetPacketInit *ToNetPacketInit() const; + protected: - NetPacketInit m_data; + void Init(); + +private: + NetPacketInitData m_data; +}; + +class NetPacketInitAck : public NetPacket +{ +public: + NetPacketInitAck(); + NetPacketInitAck(u_int32_t value); + virtual ~NetPacketInitAck(); + + virtual const NetPacketHeader *GetData() const; + virtual void SetData(const NetPacketHeader *p); + + virtual const NetPacketInitAck *ToNetPacketInitAck() const; + +protected: + void Init(); + +private: + NetPacketInitAckData m_data; +}; + +class NetPacketGameStart : public NetPacket +{ +public: + NetPacketGameStart(); + NetPacketGameStart(u_int32_t value); + virtual ~NetPacketGameStart(); + + virtual const NetPacketHeader *GetData() const; + virtual void SetData(const NetPacketHeader *p); + + virtual const NetPacketGameStart *ToNetPacketGameStart() const; + +protected: + void Init(); + +private: + NetPacketGameStartData m_data; }; #endif diff --git a/src/net/receiverhelper.h b/src/net/receiverhelper.h index 42a515d4..82090012 100644 --- a/src/net/receiverhelper.h +++ b/src/net/receiverhelper.h @@ -29,6 +29,7 @@ // MUST be larger than MAX_PACKET_SIZE #define RECV_BUF_SIZE 10 * MAX_PACKET_SIZE +#define RECV_TIMEOUT_MSEC 50 class ReceiverHelper diff --git a/src/net/netcallback.h b/src/net/servercallback.h similarity index 82% rename from src/net/netcallback.h rename to src/net/servercallback.h index 588e10fa..77cdb020 100644 --- a/src/net/netcallback.h +++ b/src/net/servercallback.h @@ -16,18 +16,18 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -/* Generic callback interface for network system. */ +/* Callback interface for network server gui. */ -#ifndef _NETCALLBACK_H_ -#define _NETCALLBACK_H_ +#ifndef _SERVERCALLBACK_H_ +#define _SERVERCALLBACK_H_ -class NetCallback +class ServerCallback { public: - virtual ~NetCallback(); + virtual ~ServerCallback(); - virtual void SignalNetSuccess(int actionID) = 0; - virtual void SignalNetError(int errorID, int osErrorID) = 0; + virtual void SignalNetServerSuccess(int actionID) = 0; + virtual void SignalNetServerError(int errorID, int osErrorID) = 0; }; #endif diff --git a/src/net/serverrecvstate.h b/src/net/serverrecvstate.h index 50c5da51..18886406 100644 --- a/src/net/serverrecvstate.h +++ b/src/net/serverrecvstate.h @@ -24,7 +24,8 @@ #include #include -#define SERVER_INITIAL_STATE ServerRecvStateInit +#define SERVER_INITIAL_STATE ServerRecvStateInit +#define SERVER_START_GAME_STATE ServerRecvStateStartGame class ServerRecvThread; class ServerCallback; @@ -35,7 +36,7 @@ public: virtual ~ServerRecvState(); // Handling of a new TCP connection. - virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data) = 0; + virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr connData) = 0; // Main processing function of the current state. virtual int Process(ServerRecvThread &server) = 0; @@ -62,4 +63,25 @@ protected: ServerRecvStateInit(); }; +// State: Start server game. +class ServerRecvStateStartGame : public ServerRecvState +{ +public: + // Access the state singleton. + static ServerRecvStateStartGame &Instance(); + + virtual ~ServerRecvStateStartGame(); + + // + virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data); + + // + virtual int Process(ServerRecvThread &server); + +protected: + + // Protected constructor - this is a singleton. + ServerRecvStateStartGame(); +}; + #endif diff --git a/src/net/serverrecvthread.h b/src/net/serverrecvthread.h index 70a1247b..bbf78b3f 100644 --- a/src/net/serverrecvthread.h +++ b/src/net/serverrecvthread.h @@ -23,14 +23,17 @@ #include #include +#include #include #include +#include class ServerRecvState; class SenderThread; class ReceiverHelper; class ServerSenderCallback; +class NetPacket; class ServerRecvThread : public Thread { @@ -38,18 +41,24 @@ public: ServerRecvThread(); virtual ~ServerRecvThread(); + void StartGame(); + void SendToAllClients(boost::shared_ptr packet); void AddConnection(boost::shared_ptr data); protected: + typedef std::map > SocketSessionMap; + // Main function of the thread. virtual void Main(); + SOCKET Select(); + ServerRecvState &GetState(); void SetState(ServerRecvState &newState); - //const ServerRecvContext &GetContext() const; - //ServerRecvContext &GetContext(); + boost::shared_ptr GetSession(SOCKET sock); + void AddSession(boost::shared_ptr connData, boost::shared_ptr sessionData); SenderThread &GetSender(); ReceiverHelper &GetReceiver(); @@ -57,16 +66,19 @@ protected: ServerSenderCallback &GetSenderCallback(); private: - //std::auto_ptr m_context; std::deque > m_connectQueue; mutable boost::mutex m_connectQueueMutex; ServerRecvState *m_curState; + SocketSessionMap m_sessions; + std::auto_ptr m_receiver; std::auto_ptr m_sender; std::auto_ptr m_senderCallback; + +friend class ServerRecvStateInit; }; #endif diff --git a/src/net/serverthread.h b/src/net/serverthread.h index dcbf53cd..bc0d0406 100644 --- a/src/net/serverthread.h +++ b/src/net/serverthread.h @@ -24,6 +24,7 @@ #include #include #include +#include class ServerContext; class ServerRecvThread; @@ -33,11 +34,14 @@ class SenderThread; class ServerThread : public Thread { public: - ServerThread(/*ServerCallback &gui*/); + ServerThread(ServerCallback &gui); virtual ~ServerThread(); // Set the parameters. void Init(unsigned serverPort, bool ipv6, const std::string &pwd); + void StartGame(); + + ServerCallback &GetCallback(); protected: @@ -55,6 +59,8 @@ protected: private: std::auto_ptr m_context; std::auto_ptr m_recvThread; + + ServerCallback &m_callback; }; #endif diff --git a/src/net/sessiondata.h b/src/net/sessiondata.h new file mode 100644 index 00000000..557e53c2 --- /dev/null +++ b/src/net/sessiondata.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2007 by Lothar May * + * * + * 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; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +/* Session data (a session is a valid client connection). */ + +#ifndef _SESSIONDATA_H_ +#define _SESSIONDATA_H_ + +#include + +#define SESSION_ID_INIT 0 + +class SessionData +{ +public: + enum State { Init, Established }; + + SessionData(); + ~SessionData(); + + unsigned GetId() const + {return m_id;} + void SetId(unsigned id) + {m_id = id;} + State GetState() const + {return m_state;} + void SetState(State state) + {m_state = state;} + + const std::string &GetClientAddr() const + {return m_clientAddr;} + void SetClientAddr(const std::string &addr) + {m_clientAddr = addr;} + +private: + unsigned m_id; + State m_state; + std::string m_clientAddr; +}; + +#endif diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index 93b93f31..abcc1cf1 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -35,16 +35,22 @@ #define ERR_SOCK_RECV_FAILED 13 #define ERR_SOCK_SEND_FAILED 14 #define ERR_SOCK_CONN_RESET 15 +#define ERR_SOCK_CONN_EXISTS 16 // This is an internal message which is not reported. #define MSG_SOCK_INTERNAL_PENDING 0 -// The following messages are reported. +// The following messages are connect messages. #define MSG_SOCK_INIT_DONE 1 #define MSG_SOCK_RESOLVE_DONE 2 #define MSG_SOCK_CONNECT_DONE 3 #define MSG_SOCK_SESSION_DONE 4 +#define MSG_SOCK_LIMIT_CONNECT MSG_SOCK_SESSION_DONE + +// The following messages are game messages. +#define MSG_SOCK_GAME_START 5 + #define MSG_SOCK_LAST MSG_SOCK_SESSION_DONE #endif diff --git a/src/session.cpp b/src/session.cpp index 7b065898..32179238 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -85,14 +85,21 @@ void Session::startNetworkServer() { if (myNetServer) return; // TODO: throw exception - myNetServer = new ServerThread(); + myNetServer = new ServerThread(*myGui); myNetServer->Init( myConfig->readConfigInt("ServerPort"), myConfig->readConfigInt("ServerUseIpv6") == 1, - ""); + ""); // TODO: use pwd myNetServer->Run(); } +void Session::initiateNetworkServerGame() +{ + if (!myNetServer) + return; // TODO: throw exception + myNetServer->StartGame(); +} + void Session::terminateNetworkServer() { if (!myNetServer) diff --git a/src/session.h b/src/session.h index 37557a8d..b9bcb23b 100755 --- a/src/session.h +++ b/src/session.h @@ -41,6 +41,7 @@ public: void terminateNetworkClient(); void startNetworkServer(); + void initiateNetworkServerGame(); void terminateNetworkServer(); void setActualGameID(const int& theValue) { actualGameID = theValue; }