From 13a731e041b2724d137ceeacc5abcd9eae7207c5 Mon Sep 17 00:00:00 2001 From: lotodore Date: Fri, 25 May 2007 16:03:12 +0000 Subject: [PATCH] Chat messages are now working. --- docs/net_protocol.txt | 58 ++++- src/gui/generic/serverguiwrapper.cpp | 3 +- src/gui/generic/serverguiwrapper.h | 3 +- src/gui/guiinterface.h | 4 - src/gui/qt/guiwrapper.cpp | 3 +- src/gui/qt/guiwrapper.h | 3 +- src/gui/qt/mainwindow/chat/chat.cpp | 7 +- src/gui/qt/mainwindow/chat/chat.h | 5 +- src/net/clientcallback.h | 4 +- src/net/clientstate.h | 44 ++-- src/net/clientthread.h | 6 +- src/net/common/clientstate.cpp | 197 +++++++------- src/net/common/clientthread.cpp | 49 +++- src/net/common/netpacket.cpp | 236 ++++++++++++++++- src/net/common/serverrecvstate.cpp | 373 ++++++++++++++------------- src/net/netpacket.h | 52 ++++ src/net/serverrecvstate.h | 37 ++- src/net/serverrecvthread.h | 1 + src/net/socket_msg.h | 1 + src/session.cpp | 10 +- src/session.h | 2 +- 21 files changed, 761 insertions(+), 337 deletions(-) diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index e9666bee..28a911dd 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -146,8 +146,6 @@ Server Request/Notification: Player's Turn +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Game State: - 0xF0 - Preflop Small Blind (no action requested) - 0xF1 - Preflop Big Blind (no action requested) 0x00 - Preflop 0x01 - Flop 0x02 - Turn @@ -168,23 +166,23 @@ Client Reply/Request: Player's Action [ Game State is confirmed to ensure consistency. ] Player Action: - 0 - None (e.g. Small Blind or Big Blind in Preflop) - 1 - Fold - 2 - Check - 3 - Call - 4 - Bet - 5 - Raise - 6 - All in + 0x00 - None (e.g. Small Blind or Big Blind in Preflop) + 0x01 - Fold + 0x02 - Check + 0x03 - Call + 0x04 - Bet + 0x05 - Raise + 0x06 - All in Player Bet: - 0 - states fold, check, call, all in - > 0 - states bet, raise + 0 (ignored) - states fold, check, call, all in + > 0 - states bet, raise Server Notification: Player's Action Done 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Message Type = 9 | Message Length = 12 | + | Message Type = 9 | Message Length = 28 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Game State | Player Id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -199,6 +197,10 @@ Server Notification: Player's Action Done | Current Hand Bets | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +Additional Game States: + 0xF0 - Preflop Small Blind (no action requested) + 0xF1 - Preflop Big Blind (no action requested) + Server Reply: Player's Action Rejected @@ -228,6 +230,38 @@ Rejection Reason: 0xFFFF - Other reason +Client Request: Send Chat Text + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Type = 11 | Message Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Text Length | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | \ + \ Text (UTF-8) / + | +-------------------------------+ + / | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +Server Notification: Chat Text + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Type = 12 | Message Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Text Length | Player Id | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | \ + \ Text (UTF-8) / + | +-------------------------------+ + / | padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Server Reply: Error 0 1 2 3 diff --git a/src/gui/generic/serverguiwrapper.cpp b/src/gui/generic/serverguiwrapper.cpp index 531a7d61..a06a2043 100644 --- a/src/gui/generic/serverguiwrapper.cpp +++ b/src/gui/generic/serverguiwrapper.cpp @@ -94,14 +94,13 @@ void ServerGuiWrapper::meInAction() {} void ServerGuiWrapper::logPlayerActionMsg(string playerName, int action, int setValue) {} void ServerGuiWrapper::logNewGameHandMsg(int gameID, int handID) {} -void ServerGuiWrapper::chatAppendMsg(std::string msg) {} - void ServerGuiWrapper::SignalNetClientConnect(int actionID) { if (myClientcb) myClientcb->SignalNetClientConnect(actionID); } void ServerGuiWrapper::SignalNetClientGameInfo(int actionID) { if (myClientcb) myClientcb->SignalNetClientGameInfo(actionID); } void ServerGuiWrapper::SignalNetClientError(int errorID, int osErrorID) { if (myClientcb) myClientcb->SignalNetClientError(errorID, osErrorID); } void ServerGuiWrapper::SignalNetClientPlayerJoined(const string &playerName) { if (myClientcb) myClientcb->SignalNetClientPlayerJoined(playerName); } void ServerGuiWrapper::SignalNetClientPlayerLeft(const string &playerName) { if (myClientcb) myClientcb->SignalNetClientPlayerLeft(playerName); } void ServerGuiWrapper::SignalNetClientGameStart(boost::shared_ptr game) { if (myClientcb) myClientcb->SignalNetClientGameStart(game); } +void ServerGuiWrapper::SignalNetClientChatMsg(const string &playerName, const string &msg) { if (myClientcb) myClientcb->SignalNetClientChatMsg(playerName, msg); } void ServerGuiWrapper::SignalNetServerSuccess(int actionID) { if (myServercb) myServercb->SignalNetServerSuccess(actionID); } void ServerGuiWrapper::SignalNetServerError(int errorID, int osErrorID) { if (myServercb) myServercb->SignalNetServerError(errorID, osErrorID); } diff --git a/src/gui/generic/serverguiwrapper.h b/src/gui/generic/serverguiwrapper.h index 7595ebcc..01f8ab52 100644 --- a/src/gui/generic/serverguiwrapper.h +++ b/src/gui/generic/serverguiwrapper.h @@ -77,13 +77,12 @@ public: void logPlayerActionMsg(std::string playerName, int action, int setValue) ; void logNewGameHandMsg(int gameID, int handID) ; - void chatAppendMsg(std::string msg); - void SignalNetClientConnect(int actionID); void SignalNetClientGameInfo(int actionID); void SignalNetClientError(int errorID, int osErrorID); void SignalNetClientPlayerJoined(const std::string &playerName); void SignalNetClientPlayerLeft(const std::string &playerName); + void SignalNetClientChatMsg(const std::string &playerName, const std::string &msg); void SignalNetClientGameStart(boost::shared_ptr game); diff --git a/src/gui/guiinterface.h b/src/gui/guiinterface.h index f15c1fb3..b97653f0 100644 --- a/src/gui/guiinterface.h +++ b/src/gui/guiinterface.h @@ -92,10 +92,6 @@ public: //log.cpp virtual void logPlayerActionMsg(std::string playName, int action, int setValue) =0; virtual void logNewGameHandMsg(int gameID, int HandID) =0; - - //chat - virtual void chatAppendMsg(std::string msg) =0; - }; #endif diff --git a/src/gui/qt/guiwrapper.cpp b/src/gui/qt/guiwrapper.cpp index 0ba4d30f..6b0980a5 100644 --- a/src/gui/qt/guiwrapper.cpp +++ b/src/gui/qt/guiwrapper.cpp @@ -92,14 +92,13 @@ void GuiWrapper::meInAction() { myW->signalMeInAction(); } void GuiWrapper::logPlayerActionMsg(string playerName, int action, int setValue) { myLog->signalLogPlayerActionMsg(QString::fromUtf8(playerName.c_str()), action, setValue); } void GuiWrapper::logNewGameHandMsg(int gameID, int handID) { myLog->signalLogNewGameHandMsg(gameID, handID); } -void GuiWrapper::chatAppendMsg(string msg) { myChat->receiveMessage(msg); } - 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::SignalNetClientPlayerJoined(const string &playerName) { myW->signalNetClientPlayerJoined(QString::fromUtf8(playerName.c_str())); } void GuiWrapper::SignalNetClientPlayerLeft(const string &playerName) { myW->signalNetClientPlayerLeft(QString::fromUtf8(playerName.c_str())); } void GuiWrapper::SignalNetClientGameStart(boost::shared_ptr game) { myW->signalNetClientGameStart(game); } +void GuiWrapper::SignalNetClientChatMsg(const string &playerName, const string &msg) { myChat->signalChatMessage(QString::fromUtf8(playerName.c_str()), QString::fromUtf8(msg.c_str())); } 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 ff4a8d41..148ac6a2 100644 --- a/src/gui/qt/guiwrapper.h +++ b/src/gui/qt/guiwrapper.h @@ -85,13 +85,12 @@ public: void logPlayerActionMsg(std::string playerName, int action, int setValue) ; void logNewGameHandMsg(int gameID, int handID) ; - void chatAppendMsg(std::string msg); - void SignalNetClientConnect(int actionID); void SignalNetClientGameInfo(int actionID); void SignalNetClientError(int errorID, int osErrorID); void SignalNetClientPlayerJoined(const std::string &playerName); void SignalNetClientPlayerLeft(const std::string &playerName); + void SignalNetClientChatMsg(const std::string &playerName, const std::string &msg); void SignalNetClientGameStart(boost::shared_ptr game); diff --git a/src/gui/qt/mainwindow/chat/chat.cpp b/src/gui/qt/mainwindow/chat/chat.cpp index c52ec9a7..c81638c9 100644 --- a/src/gui/qt/mainwindow/chat/chat.cpp +++ b/src/gui/qt/mainwindow/chat/chat.cpp @@ -27,7 +27,8 @@ using namespace std; Chat::Chat(mainWindowImpl* w, ConfigFile *c) : myW(w), myConfig(c) { myW->setChat(this); - + + connect(this, SIGNAL(signalChatMessage(QString, QString)), this, SLOT(receiveMessage(QString, QString))); } Chat::~Chat() @@ -42,9 +43,9 @@ void Chat::sendMessage() { myW->lineEdit_ChatInput->setText(""); } -void Chat::receiveMessage(std::string message) { +void Chat::receiveMessage(QString playerName, QString message) { - myW->textBrowser_Chat->append(QString::fromUtf8(message.c_str())); + myW->textBrowser_Chat->append(playerName + ": " + message); checkInvisible(); } diff --git a/src/gui/qt/mainwindow/chat/chat.h b/src/gui/qt/mainwindow/chat/chat.h index 1fa4d6ac..726d6013 100644 --- a/src/gui/qt/mainwindow/chat/chat.h +++ b/src/gui/qt/mainwindow/chat/chat.h @@ -39,10 +39,13 @@ public: ~Chat(); +signals: + void signalChatMessage(QString playerName, QString msg); + public slots: void sendMessage(); - void receiveMessage(std::string); + void receiveMessage(QString playerName, QString msg); void checkInvisible(); private: diff --git a/src/net/clientcallback.h b/src/net/clientcallback.h index d7817b34..30a67f97 100644 --- a/src/net/clientcallback.h +++ b/src/net/clientcallback.h @@ -22,7 +22,7 @@ #define _CLIENTCALLBACK_H_ #include -#include +#include class Game; @@ -38,6 +38,8 @@ public: virtual void SignalNetClientGameStart(boost::shared_ptr game) = 0; virtual void SignalNetClientPlayerJoined(const std::string &playerName) = 0; virtual void SignalNetClientPlayerLeft(const std::string &playerName) = 0; + + virtual void SignalNetClientChatMsg(const std::string &playerName, const std::string &msg) = 0; }; #endif diff --git a/src/net/clientstate.h b/src/net/clientstate.h index c64dba5e..3e16ea64 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -32,6 +32,7 @@ class ClientThread; class ClientCallback; class ResolverThread; class Game; +class NetPacket; class ClientState { @@ -166,8 +167,24 @@ protected: ClientStateStartSession(); }; +// Abstract State: Receiving +class AbstractClientStateReceiving : public ClientState +{ +public: + virtual ~AbstractClientStateReceiving(); + + // select on socket. + virtual int Process(ClientThread &client); + +protected: + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet) = 0; + + AbstractClientStateReceiving(); +}; + // State: Wait for Session ACK. -class ClientStateWaitSession : public ClientState +class ClientStateWaitSession : public AbstractClientStateReceiving { public: // Access the state singleton. @@ -175,17 +192,17 @@ public: virtual ~ClientStateWaitSession(); - // select on socket. - virtual int Process(ClientThread &client); protected: // Protected constructor - this is a singleton. ClientStateWaitSession(); + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); }; // State: Wait for start of the game or start info. -class ClientStateWaitGame : public ClientState +class ClientStateWaitGame : public AbstractClientStateReceiving { public: // Access the state singleton. @@ -193,17 +210,16 @@ public: virtual ~ClientStateWaitGame(); - // select on socket. - virtual int Process(ClientThread &client); - protected: // Protected constructor - this is a singleton. ClientStateWaitGame(); + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); }; // State: Wait for start of the next hand. -class ClientStateWaitHand : public ClientState +class ClientStateWaitHand : public AbstractClientStateReceiving { public: // Access the state singleton. @@ -211,17 +227,16 @@ public: virtual ~ClientStateWaitHand(); - // select on socket. - virtual int Process(ClientThread &client); - protected: // Protected constructor - this is a singleton. ClientStateWaitHand(); + + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); }; // State: Hand Loop. -class ClientStateRunHand : public ClientState +class ClientStateRunHand : public AbstractClientStateReceiving { public: // Access the state singleton. @@ -229,14 +244,13 @@ public: virtual ~ClientStateRunHand(); - // select on socket. - virtual int Process(ClientThread &client); - protected: // Protected constructor - this is a singleton. ClientStateRunHand(); + virtual int InternalProcess(ClientThread &client, boost::shared_ptr packet); + static int GetHighestSet(Game &curGame); static void SetHighestSet(Game &curGame, int highestSet); static int GetPlayersTurn(Game &curGame); diff --git a/src/net/clientthread.h b/src/net/clientthread.h index 359e54f4..3d801358 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -52,6 +52,7 @@ public: const std::string &playerName); void SendPlayerAction(); + void SendChatMessage(const std::string &msg); ClientCallback &GetCallback(); GuiInterface &GetGui(); @@ -84,7 +85,9 @@ protected: void AddPlayerData(boost::shared_ptr playerData); void RemovePlayerData(unsigned playerId); - PlayerDataList GetMappedPlayerDataList() const; + void MapPlayerDataList(); + const PlayerDataList &GetPlayerDataList() const; + boost::shared_ptr GetPlayerDataByUniqueId(unsigned id); private: @@ -105,6 +108,7 @@ private: unsigned m_curGameId; int m_guiPlayerNum; +friend class AbstractClientStateReceiving; friend class ClientStateInit; friend class ClientStateStartResolve; friend class ClientStateResolving; diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index f1a8844d..5ac13b5e 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -357,6 +357,43 @@ ClientStateStartSession::Process(ClientThread &client) //----------------------------------------------------------------------------- +AbstractClientStateReceiving::AbstractClientStateReceiving() +{ +} + +AbstractClientStateReceiving::~AbstractClientStateReceiving() +{ +} + +int +AbstractClientStateReceiving::Process(ClientThread &client) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + + // delegate to receiver helper class + boost::shared_ptr tmpPacket = client.GetReceiver().Recv(client.GetContext().GetSocket()); + + if (tmpPacket.get()) + { + if (tmpPacket->ToNetPacketChatText()) + { + // Chat message - display it in the GUI. + NetPacketChatText::Data chatData; + tmpPacket->ToNetPacketChatText()->GetData(chatData); + + boost::shared_ptr tmpPlayer = client.GetPlayerDataByUniqueId(chatData.playerId); + if (tmpPlayer.get()) + client.GetCallback().SignalNetClientChatMsg(tmpPlayer->GetName(), chatData.text); + } + else + retVal = InternalProcess(client, tmpPacket); + } + + return retVal; +} + +//----------------------------------------------------------------------------- + ClientStateWaitSession & ClientStateWaitSession::Instance() { @@ -373,43 +410,36 @@ ClientStateWaitSession::~ClientStateWaitSession() } int -ClientStateWaitSession::Process(ClientThread &client) +ClientStateWaitSession::InternalProcess(ClientThread &client, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; ClientContext &context = client.GetContext(); - // delegate to receiver helper class - - boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - - if (tmpPacket.get()) + if (packet->ToNetPacketJoinGameAck()) { - if (tmpPacket->ToNetPacketJoinGameAck()) - { - // Everything is fine - we joined the game. - // Initialize game configuration. - NetPacketJoinGameAck::Data joinGameAckData; - tmpPacket->ToNetPacketJoinGameAck()->GetData(joinGameAckData); - client.SetGameData(joinGameAckData.gameData); - client.SetGuiPlayerNum(joinGameAckData.yourPlayerNum); + // Everything is fine - we joined the game. + // Initialize game configuration. + NetPacketJoinGameAck::Data joinGameAckData; + packet->ToNetPacketJoinGameAck()->GetData(joinGameAckData); + client.SetGameData(joinGameAckData.gameData); + client.SetGuiPlayerNum(joinGameAckData.yourPlayerNum); - // TODO: Type Human is fixed here. - boost::shared_ptr playerData( - new PlayerData(joinGameAckData.yourPlayerUniqueId, joinGameAckData.yourPlayerNum, PLAYER_TYPE_HUMAN)); - playerData->SetName(context.GetPlayerName()); - client.AddPlayerData(playerData); + // TODO: Type Human is fixed here. + boost::shared_ptr playerData( + new PlayerData(joinGameAckData.yourPlayerUniqueId, joinGameAckData.yourPlayerNum, PLAYER_TYPE_HUMAN)); + playerData->SetName(context.GetPlayerName()); + client.AddPlayerData(playerData); - client.SetState(ClientStateWaitGame::Instance()); - retVal = MSG_SOCK_SESSION_DONE; - } - else if (tmpPacket->ToNetPacketError()) - { - // Server reported an error. - NetPacketError::Data errorData; - tmpPacket->ToNetPacketError()->GetData(errorData); - // Show the error. - throw ClientException(errorData.errorCode, 0); - } + client.SetState(ClientStateWaitGame::Instance()); + retVal = MSG_SOCK_SESSION_DONE; + } + else if (packet->ToNetPacketError()) + { + // Server reported an error. + NetPacketError::Data errorData; + packet->ToNetPacketError()->GetData(errorData); + // Show the error. + throw ClientException(errorData.errorCode, 0); } return retVal; @@ -433,45 +463,39 @@ ClientStateWaitGame::~ClientStateWaitGame() } int -ClientStateWaitGame::Process(ClientThread &client) +ClientStateWaitGame::InternalProcess(ClientThread &client, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; ClientContext &context = client.GetContext(); - // delegate to receiver helper class - boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - - if (tmpPacket.get()) + if (packet->ToNetPacketGameStart()) { - if (tmpPacket->ToNetPacketGameStart()) - { - // Start the network game as client. - NetPacketGameStart::Data gameStartData; - tmpPacket->ToNetPacketGameStart()->GetData(gameStartData); + // Start the network game as client. + NetPacketGameStart::Data gameStartData; + packet->ToNetPacketGameStart()->GetData(gameStartData); - client.SetStartData(gameStartData.startData); + client.SetStartData(gameStartData.startData); - client.SetState(ClientStateWaitHand::Instance()); - retVal = MSG_NET_GAME_CLIENT_START; - } - else if (tmpPacket->ToNetPacketPlayerJoined()) - { - // Another player joined the network game. - NetPacketPlayerJoined::Data netPlayerData; - tmpPacket->ToNetPacketPlayerJoined()->GetData(netPlayerData); + client.SetState(ClientStateWaitHand::Instance()); + retVal = MSG_NET_GAME_CLIENT_START; + } + else if (packet->ToNetPacketPlayerJoined()) + { + // Another player joined the network game. + NetPacketPlayerJoined::Data netPlayerData; + packet->ToNetPacketPlayerJoined()->GetData(netPlayerData); - boost::shared_ptr playerData( - new PlayerData(netPlayerData.playerId, netPlayerData.playerNumber, netPlayerData.ptype)); - playerData->SetName(netPlayerData.playerName); - client.AddPlayerData(playerData); - } - else if (tmpPacket->ToNetPacketPlayerLeft()) - { - // Another player left the network game. - NetPacketPlayerLeft::Data netPlayerData; - tmpPacket->ToNetPacketPlayerLeft()->GetData(netPlayerData); - client.RemovePlayerData(netPlayerData.playerId); - } + boost::shared_ptr playerData( + new PlayerData(netPlayerData.playerId, netPlayerData.playerNumber, netPlayerData.ptype)); + playerData->SetName(netPlayerData.playerName); + client.AddPlayerData(playerData); + } + else if (packet->ToNetPacketPlayerLeft()) + { + // Another player left the network game. + NetPacketPlayerLeft::Data netPlayerData; + packet->ToNetPacketPlayerLeft()->GetData(netPlayerData); + client.RemovePlayerData(netPlayerData.playerId); } // TODO: handle error packet @@ -496,33 +520,27 @@ ClientStateWaitHand::~ClientStateWaitHand() } int -ClientStateWaitHand::Process(ClientThread &client) +ClientStateWaitHand::InternalProcess(ClientThread &client, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; ClientContext &context = client.GetContext(); - // Delegate to receiver helper class. - boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - - if (tmpPacket.get()) + if (packet->ToNetPacketHandStart()) { - if (tmpPacket->ToNetPacketHandStart()) - { - // Hand was started. - // These are the cards. Good luck. - NetPacketHandStart::Data tmpData; - tmpPacket->ToNetPacketHandStart()->GetData(tmpData); - int myCards[2]; - myCards[0] = (int)tmpData.yourCards[0]; - myCards[1] = (int)tmpData.yourCards[1]; - client.GetGame()->getPlayerArray()[0]->setMyCards(myCards); - client.GetGame()->initHand(); - client.GetGame()->startHand(); - client.GetGui().dealHoleCards(); - client.SetState(ClientStateRunHand::Instance()); + // Hand was started. + // These are the cards. Good luck. + NetPacketHandStart::Data tmpData; + packet->ToNetPacketHandStart()->GetData(tmpData); + int myCards[2]; + myCards[0] = (int)tmpData.yourCards[0]; + myCards[1] = (int)tmpData.yourCards[1]; + client.GetGame()->getPlayerArray()[0]->setMyCards(myCards); + client.GetGame()->initHand(); + client.GetGame()->startHand(); + client.GetGui().dealHoleCards(); + client.SetState(ClientStateRunHand::Instance()); - retVal = MSG_NET_GAME_CLIENT_HAND; - } + retVal = MSG_NET_GAME_CLIENT_HAND; } return MSG_SOCK_INTERNAL_PENDING; @@ -546,20 +564,17 @@ ClientStateRunHand::~ClientStateRunHand() } int -ClientStateRunHand::Process(ClientThread &client) +ClientStateRunHand::InternalProcess(ClientThread &client, boost::shared_ptr packet) { ClientContext &context = client.GetContext(); - // Delegate to receiver helper class. - boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - - if (tmpPacket.get()) + if (packet.get()) { boost::shared_ptr curGame = client.GetGame(); - if (tmpPacket->ToNetPacketPlayersActionDone()) + if (packet->ToNetPacketPlayersActionDone()) { NetPacketPlayersActionDone::Data actionDoneData; - tmpPacket->ToNetPacketPlayersActionDone()->GetData(actionDoneData); + packet->ToNetPacketPlayersActionDone()->GetData(actionDoneData); PlayerInterface *tmpPlayer = curGame->getPlayerByUniqueId(actionDoneData.playerId); assert(tmpPlayer); // TODO: throw exception @@ -587,10 +602,10 @@ ClientStateRunHand::Process(ClientThread &client) client.GetGui().refreshAction(); client.GetGui().refreshCash(); } - else if (tmpPacket->ToNetPacketPlayersTurn()) + else if (packet->ToNetPacketPlayersTurn()) { NetPacketPlayersTurn::Data turnData; - tmpPacket->ToNetPacketPlayersTurn()->GetData(turnData); + packet->ToNetPacketPlayersTurn()->GetData(turnData); PlayerInterface *tmpPlayer = curGame->getPlayerByUniqueId(turnData.playerId); assert(tmpPlayer); // TODO: throw exception diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index d1c9b99f..2c465fcd 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -96,6 +96,19 @@ ClientThread::SendPlayerAction() GetSender().Send(GetContext().GetSocket(), action); } +void +ClientThread::SendChatMessage(const std::string &msg) +{ + // Warning: This function is called in the context of the GUI thread. + // Create a network packet containing the chat message. + boost::shared_ptr chat(new NetPacketSendChatText); + NetPacketSendChatText::Data chatData; + chatData.text = msg; + static_cast(chat.get())->SetData(chatData); + // The sender is thread-safe, so just dump the packet. + GetSender().Send(GetContext().GetSocket(), chat); +} + ClientCallback & ClientThread::GetCallback() { @@ -133,7 +146,8 @@ ClientThread::Main() // EngineFactory erstellen boost::shared_ptr factory(new ClientEngineFactory); // LocalEngine erstellen - m_game.reset(new Game(&m_gui, factory, GetMappedPlayerDataList(), GetGameData(), GetStartData(), m_curGameId++)); + MapPlayerDataList(); + m_game.reset(new Game(&m_gui, factory, GetPlayerDataList(), GetGameData(), GetStartData(), m_curGameId++)); GetCallback().SignalNetClientGameStart(m_game); } } @@ -268,8 +282,8 @@ ClientThread::RemovePlayerData(unsigned playerId) GetCallback().SignalNetClientPlayerLeft(playerName); } -PlayerDataList -ClientThread::GetMappedPlayerDataList() const +void +ClientThread::MapPlayerDataList() { PlayerDataList mappedList; @@ -293,6 +307,33 @@ ClientThread::GetMappedPlayerDataList() const // Sort the list by player number. mappedList.sort(*boost::lambda::_1 < *boost::lambda::_2); - return mappedList; + m_playerDataList = mappedList; + SetGuiPlayerNum(0); +} + +const PlayerDataList & +ClientThread::GetPlayerDataList() const +{ + return m_playerDataList; +} + +boost::shared_ptr +ClientThread::GetPlayerDataByUniqueId(unsigned id) +{ + boost::shared_ptr tmpPlayer; + + PlayerDataList::const_iterator i = m_playerDataList.begin(); + PlayerDataList::const_iterator end = m_playerDataList.end(); + + while (i != end) + { + if ((*i)->GetUniqueId() == id) + { + tmpPlayer = *i; + break; + } + ++i; + } + return tmpPlayer; } diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index 4c57bd79..63233be4 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -37,6 +37,8 @@ using namespace std; #define NET_TYPE_PLAYERS_ACTION 0x0008 #define NET_TYPE_PLAYERS_ACTION_DONE 0x0009 #define NET_TYPE_PLAYERS_ACTION_REJECTED 0x000A +#define NET_TYPE_SEND_CHAT_TEXT 0x000B +#define NET_TYPE_CHAT_TEXT 0x000C #define NET_TYPE_ERROR 0x0400 @@ -75,7 +77,6 @@ struct GCC_PACKED NetPacketJoinGameData u_int16_t playerFlags; u_int16_t playerNameLength; u_int16_t reserved; - char password[1]; }; struct GCC_PACKED NetPacketJoinGameAckData @@ -105,7 +106,6 @@ struct GCC_PACKED NetPacketPlayerJoinedData u_int16_t playerNumber; u_int16_t playerFlags; u_int16_t playerNameLength; - char playerName[1]; }; struct GCC_PACKED NetPacketPlayerLeftData @@ -166,6 +166,21 @@ struct GCC_PACKED NetPacketPlayersActionRejectedData u_int16_t reserved; }; +struct GCC_PACKED NetPacketSendChatTextData +{ + NetPacketHeader head; + u_int16_t textLength; + u_int16_t reserved; +}; + +struct GCC_PACKED NetPacketChatTextData +{ + NetPacketHeader head; + u_int16_t textLength; + u_int16_t playerId; + char text[1]; +}; + struct GCC_PACKED NetPacketErrorData { NetPacketHeader head; @@ -229,6 +244,12 @@ NetPacket::Create(char *data, unsigned &dataSize) case NET_TYPE_PLAYERS_ACTION_REJECTED: tmpPacket = boost::shared_ptr(new NetPacketPlayersActionRejected); break; + case NET_TYPE_SEND_CHAT_TEXT: + tmpPacket = boost::shared_ptr(new NetPacketSendChatText); + break; + case NET_TYPE_CHAT_TEXT: + tmpPacket = boost::shared_ptr(new NetPacketChatText); + break; case NET_TYPE_ERROR: tmpPacket = boost::shared_ptr(new NetPacketError); break; @@ -373,6 +394,18 @@ NetPacket::ToNetPacketPlayersActionRejected() const return NULL; } +const NetPacketSendChatText * +NetPacket::ToNetPacketSendChatText() const +{ + return NULL; +} + +const NetPacketChatText * +NetPacket::ToNetPacketChatText() const +{ + return NULL; +} + const NetPacketError * NetPacket::ToNetPacketError() const { @@ -461,8 +494,9 @@ NetPacketJoinGame::SetData(const NetPacketJoinGame::Data &inData) tmpData->passwordLength = htons(passwordLen); tmpData->playerFlags = htons((inData.ptype == PLAYER_TYPE_HUMAN) ? NET_PLAYER_FLAG_HUMAN : 0); tmpData->playerNameLength = htons(playerNameLen); - memcpy(tmpData->password, inData.password.c_str(), passwordLen); - memcpy(tmpData->password + ADD_PADDING(passwordLen), inData.playerName.c_str(), playerNameLen); + char *passwordPtr = (char *)tmpData + sizeof(NetPacketJoinGameData); + memcpy(passwordPtr, inData.password.c_str(), passwordLen); + memcpy(passwordPtr + ADD_PADDING(passwordLen), inData.playerName.c_str(), playerNameLen); } void @@ -478,8 +512,9 @@ NetPacketJoinGame::GetData(NetPacketJoinGame::Data &outData) const outData.ptype = (ntohs(tmpData->playerFlags) & NET_PLAYER_FLAG_HUMAN) ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER; u_int16_t passwordLen = ntohs(tmpData->passwordLength); - outData.password = string(tmpData->password, passwordLen); - outData.playerName = string(tmpData->password + ADD_PADDING(passwordLen), ntohs(tmpData->playerNameLength)); + char *passwordPtr = (char *)tmpData + sizeof(NetPacketJoinGameData); + outData.password = string(passwordPtr, passwordLen); + outData.playerName = string(passwordPtr + ADD_PADDING(passwordLen), ntohs(tmpData->playerNameLength)); } const NetPacketJoinGame * @@ -640,7 +675,8 @@ NetPacketPlayerJoined::SetData(const NetPacketPlayerJoined::Data &inData) tmpData->playerId = htons(inData.playerId); tmpData->playerNumber = htons(inData.playerNumber); tmpData->playerNameLength = htons(playerNameLen); - memcpy(tmpData->playerName, inData.playerName.c_str(), playerNameLen); + char *namePtr = (char *)tmpData + sizeof(NetPacketPlayerJoinedData); + memcpy(namePtr, inData.playerName.c_str(), playerNameLen); } void @@ -653,7 +689,8 @@ NetPacketPlayerJoined::GetData(NetPacketPlayerJoined::Data &outData) const outData.ptype = (ntohs(tmpData->playerFlags) & NET_PLAYER_FLAG_HUMAN) ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER; outData.playerId = ntohs(tmpData->playerId); outData.playerNumber = ntohs(tmpData->playerNumber); - outData.playerName = string(tmpData->playerName, ntohs(tmpData->playerNameLength)); + char *namePtr = (char *)tmpData + sizeof(NetPacketPlayerJoinedData); + outData.playerName = string(namePtr, ntohs(tmpData->playerNameLength)); } const NetPacketPlayerJoined * @@ -1196,6 +1233,189 @@ NetPacketPlayersActionRejected::Check(const NetPacketHeader* data) const //----------------------------------------------------------------------------- +NetPacketSendChatText::NetPacketSendChatText() +: NetPacket(NET_TYPE_SEND_CHAT_TEXT, sizeof(NetPacketSendChatTextData)) +{ +} + +NetPacketSendChatText::~NetPacketSendChatText() +{ +} + +boost::shared_ptr +NetPacketSendChatText::Clone() const +{ + boost::shared_ptr newPacket(new NetPacketSendChatText); + try + { + newPacket->SetRawData(GetRawData()); + } catch (const NetException &) + { + // Need to return the new packet anyway. + } + return newPacket; +} + +void +NetPacketSendChatText::SetData(const NetPacketSendChatText::Data &inData) +{ + u_int16_t textLen = (u_int16_t)inData.text.length(); + + if (!textLen || textLen > MAX_CHAT_TEXT_SIZE) + throw NetException(ERR_NET_INVALID_CHAT_TEXT, 0); + + // Resize the packet so that the data fits in. + Resize((u_int16_t) + (sizeof(NetPacketSendChatTextData) + ADD_PADDING(textLen))); + + NetPacketSendChatTextData *tmpData = (NetPacketSendChatTextData *)GetRawData(); + assert(tmpData); + + // Set the data. + tmpData->textLength = htons(textLen); + char *textPtr = (char *)tmpData + sizeof(NetPacketSendChatTextData); + memcpy(textPtr, inData.text.c_str(), textLen); +} + +void +NetPacketSendChatText::GetData(NetPacketSendChatText::Data &outData) const +{ + // We assume that the data is valid. Validity has already been checked. + NetPacketSendChatTextData *tmpData = (NetPacketSendChatTextData *)GetRawData(); + assert(tmpData); + + char *textPtr = (char *)tmpData + sizeof(NetPacketSendChatTextData); + outData.text = string(textPtr, ntohs(tmpData->textLength)); +} + +const NetPacketSendChatText * +NetPacketSendChatText::ToNetPacketSendChatText() const +{ + return this; +} + +void +NetPacketSendChatText::Check(const NetPacketHeader* data) const +{ + assert(data); + + u_int16_t dataLen = ntohs(data->length); + if (dataLen < sizeof(NetPacketSendChatTextData)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + + NetPacketSendChatTextData *tmpData = (NetPacketSendChatTextData *)data; + int textLength = ntohs(tmpData->textLength); + // Generous checking - larger packets are allowed. + if (dataLen < + sizeof(NetPacketSendChatTextData) + + ADD_PADDING(textLength)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + // Check string sizes. + if (!textLength + || textLength > MAX_CHAT_TEXT_SIZE) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } +} + +//----------------------------------------------------------------------------- + +NetPacketChatText::NetPacketChatText() +: NetPacket(NET_TYPE_CHAT_TEXT, sizeof(NetPacketChatTextData)) +{ +} + +NetPacketChatText::~NetPacketChatText() +{ +} + +boost::shared_ptr +NetPacketChatText::Clone() const +{ + boost::shared_ptr newPacket(new NetPacketChatText); + try + { + newPacket->SetRawData(GetRawData()); + } catch (const NetException &) + { + // Need to return the new packet anyway. + } + return newPacket; +} + +void +NetPacketChatText::SetData(const NetPacketChatText::Data &inData) +{ + u_int16_t textLen = (u_int16_t)inData.text.length(); + + if (!textLen || textLen > MAX_CHAT_TEXT_SIZE) + throw NetException(ERR_NET_INVALID_CHAT_TEXT, 0); + + // Resize the packet so that the data fits in. + Resize((u_int16_t) + (sizeof(NetPacketChatTextData) + ADD_PADDING(textLen))); + + NetPacketChatTextData *tmpData = (NetPacketChatTextData *)GetRawData(); + assert(tmpData); + + // Set the data. + tmpData->playerId = htons(inData.playerId); + tmpData->textLength = htons(textLen); + char *textPtr = (char *)tmpData + sizeof(NetPacketChatTextData); + memcpy(textPtr, inData.text.c_str(), textLen); +} + +void +NetPacketChatText::GetData(NetPacketChatText::Data &outData) const +{ + // We assume that the data is valid. Validity has already been checked. + NetPacketChatTextData *tmpData = (NetPacketChatTextData *)GetRawData(); + assert(tmpData); + + outData.playerId = ntohs(tmpData->playerId); + char *textPtr = (char *)tmpData + sizeof(NetPacketChatTextData); + outData.text = string(textPtr, ntohs(tmpData->textLength)); +} + +const NetPacketChatText * +NetPacketChatText::ToNetPacketChatText() const +{ + return this; +} + +void +NetPacketChatText::Check(const NetPacketHeader* data) const +{ + assert(data); + + u_int16_t dataLen = ntohs(data->length); + if (dataLen < sizeof(NetPacketChatTextData)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + + NetPacketChatTextData *tmpData = (NetPacketChatTextData *)data; + int textLength = ntohs(tmpData->textLength); + // Generous checking - larger packets are allowed. + if (dataLen < + sizeof(NetPacketChatTextData) + + ADD_PADDING(textLength)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + // Check string sizes. + if (!textLength + || textLength > MAX_CHAT_TEXT_SIZE) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } +} +//----------------------------------------------------------------------------- + NetPacketError::NetPacketError() : NetPacket(NET_TYPE_ERROR, sizeof(NetPacketErrorData)) { diff --git a/src/net/common/serverrecvstate.cpp b/src/net/common/serverrecvstate.cpp index 5e05e6f6..e45b7f6d 100644 --- a/src/net/common/serverrecvstate.cpp +++ b/src/net/common/serverrecvstate.cpp @@ -41,6 +41,66 @@ ServerRecvState::~ServerRecvState() //----------------------------------------------------------------------------- +ServerRecvStateReceiving::ServerRecvStateReceiving() +{ +} + +ServerRecvStateReceiving::~ServerRecvStateReceiving() +{ +} + +int +ServerRecvStateReceiving::Process(ServerRecvThread &server) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + SOCKET recvSock = server.Select(); + + if (recvSock != INVALID_SOCKET) + { + SessionWrapper session = server.GetSession(recvSock); + boost::shared_ptr packet; + try + { + packet = server.GetReceiver().Recv(recvSock); + } catch (const NetException &) + { + if (session.sessionData.get()) + { + // TODO: Deactivate player. + server.CloseSessionDelayed(session); + return retVal; + } + } + + // Ignore if no session / no packet. + if (packet.get() && session.sessionData.get()) + { + if (packet->ToNetPacketSendChatText()) // Chat text is always allowed. + { + if (session.playerData.get()) // Only forward if this player is known. + { + // Forward chat text to all players. + // TODO: Some limitation needed. + NetPacketSendChatText::Data inChatData; + packet->ToNetPacketSendChatText()->GetData(inChatData); + + boost::shared_ptr outChat(new NetPacketChatText); + NetPacketChatText::Data outChatData; + outChatData.playerId = session.playerData->GetUniqueId(); + outChatData.text = inChatData.text; + static_cast(outChat.get())->SetData(outChatData); + server.SendToAllPlayers(outChat); + } + } + else + retVal = InternalProcess(server, session, packet); // Let other class handle this. + } + } + return retVal; +} + +//----------------------------------------------------------------------------- + ServerRecvStateInit & ServerRecvStateInit::Instance() { @@ -71,137 +131,116 @@ ServerRecvStateInit::HandleNewConnection(ServerRecvThread &server, boost::shared } int -ServerRecvStateInit::Process(ServerRecvThread &server) +ServerRecvStateInit::InternalProcess(ServerRecvThread &server, SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INIT_DONE; - SOCKET recvSock = server.Select(); - if (recvSock != INVALID_SOCKET) + // Session should be in initial state. + if (session.sessionData->GetState() != SessionData::Init) { - SessionWrapper session = server.GetSession(recvSock); - boost::shared_ptr packet; - try - { - packet = server.GetReceiver().Recv(recvSock); - } catch (const NetException &) - { - if (session.sessionData.get()) - { - server.CloseSessionDelayed(session); - return retVal; - } - } - - // Ignore if no session / no packet. - if (packet.get() && session.sessionData.get()) - { - // Session should be in initial state. - if (session.sessionData->GetState() != SessionData::Init) - { - server.SessionError(session, ERR_SOCK_INVALID_STATE); - return retVal; - } - - // Only accept join game packets. - const NetPacketJoinGame *tmpPacket = packet->ToNetPacketJoinGame(); - if (!tmpPacket) - { - server.SessionError(session, ERR_SOCK_INVALID_PACKET); - return retVal; - } - - NetPacketJoinGame::Data joinGameData; - tmpPacket->GetData(joinGameData); - - // Check the protocol version. - if (joinGameData.versionMajor != NET_VERSION_MAJOR) - { - server.SessionError(session, ERR_NET_VERSION_NOT_SUPPORTED); - return retVal; - } - - size_t curNumPlayers = server.GetCurNumberOfPlayers(); - - // Check the number of players. - if (curNumPlayers >= (size_t)server.GetGameData().numberOfPlayers) - { - server.SessionError(session, ERR_NET_SERVER_FULL); - return retVal; - } - - // Check the server password. - if (!server.CheckPassword(joinGameData.password)) - { - server.SessionError(session, ERR_NET_INVALID_PASSWORD); - return retVal; - } - - // Check whether the player name is correct. - // Paranoia check, this is also done in netpacket. - if (joinGameData.playerName.empty() || joinGameData.playerName.size() > MAX_NAME_SIZE) - { - server.SessionError(session, ERR_NET_INVALID_PLAYER_NAME); - return retVal; - } - - // Check whether this player is already connected. - if (server.IsPlayerConnected(joinGameData.playerName)) - { - server.SessionError(session, ERR_NET_PLAYER_NAME_IN_USE); - return retVal; - } - - // Create player data object. - boost::shared_ptr tmpPlayerData( - new PlayerData(m_curUniquePlayerId++, server.GetNextPlayerNumber(), joinGameData.ptype)); - tmpPlayerData->SetName(joinGameData.playerName); - tmpPlayerData->SetNetSessionData(session.sessionData); - - // Send ACK to client. - boost::shared_ptr answer(new NetPacketJoinGameAck); - NetPacketJoinGameAck::Data joinGameAckData; - joinGameAckData.sessionId = session.sessionData->GetId(); // TODO: currently unused. - joinGameAckData.yourPlayerUniqueId = tmpPlayerData->GetUniqueId(); - joinGameAckData.yourPlayerNum = tmpPlayerData->GetNumber(); - joinGameAckData.gameData = server.GetGameData(); - static_cast(answer.get())->SetData(joinGameAckData); - server.GetSender().Send(recvSock, answer); - - // Send notifications for connected players to client. - PlayerDataList tmpPlayerList = server.GetPlayerDataList(); - PlayerDataList::iterator player_i = tmpPlayerList.begin(); - PlayerDataList::iterator player_end = tmpPlayerList.end(); - while (player_i != player_end) - { - boost::shared_ptr otherPlayerJoined(new NetPacketPlayerJoined); - NetPacketPlayerJoined::Data otherPlayerJoinedData; - otherPlayerJoinedData.playerId = (*player_i)->GetUniqueId(); - otherPlayerJoinedData.playerName = (*player_i)->GetName(); - otherPlayerJoinedData.playerNumber = (*player_i)->GetNumber(); - otherPlayerJoinedData.ptype = (*player_i)->GetType(); - static_cast(otherPlayerJoined.get())->SetData(otherPlayerJoinedData); - server.GetSender().Send(session.sessionData->GetSocket(), otherPlayerJoined); - - ++player_i; - } - - // Send "Player Joined" to other fully connected clients. - boost::shared_ptr thisPlayerJoined(new NetPacketPlayerJoined); - NetPacketPlayerJoined::Data thisPlayerJoinedData; - thisPlayerJoinedData.playerId = tmpPlayerData->GetUniqueId(); - thisPlayerJoinedData.playerName = tmpPlayerData->GetName(); - thisPlayerJoinedData.playerNumber = tmpPlayerData->GetNumber(); - thisPlayerJoinedData.ptype = tmpPlayerData->GetType(); - static_cast(thisPlayerJoined.get())->SetData(thisPlayerJoinedData); - server.SendToAllPlayers(thisPlayerJoined); - - // Set player data for session. - server.SetSessionPlayerData(session.sessionData, tmpPlayerData); - - // Session is now established. - session.sessionData->SetState(SessionData::Established); - } + server.SessionError(session, ERR_SOCK_INVALID_STATE); + return retVal; } + + // Only accept join game packets. + const NetPacketJoinGame *tmpPacket = packet->ToNetPacketJoinGame(); + if (!tmpPacket) + { + server.SessionError(session, ERR_SOCK_INVALID_PACKET); + return retVal; + } + + NetPacketJoinGame::Data joinGameData; + tmpPacket->GetData(joinGameData); + + // Check the protocol version. + if (joinGameData.versionMajor != NET_VERSION_MAJOR) + { + server.SessionError(session, ERR_NET_VERSION_NOT_SUPPORTED); + return retVal; + } + + size_t curNumPlayers = server.GetCurNumberOfPlayers(); + + // Check the number of players. + if (curNumPlayers >= (size_t)server.GetGameData().numberOfPlayers) + { + server.SessionError(session, ERR_NET_SERVER_FULL); + return retVal; + } + + // Check the server password. + if (!server.CheckPassword(joinGameData.password)) + { + server.SessionError(session, ERR_NET_INVALID_PASSWORD); + return retVal; + } + + // Check whether the player name is correct. + // Paranoia check, this is also done in netpacket. + if (joinGameData.playerName.empty() || joinGameData.playerName.size() > MAX_NAME_SIZE) + { + server.SessionError(session, ERR_NET_INVALID_PLAYER_NAME); + return retVal; + } + + // Check whether this player is already connected. + if (server.IsPlayerConnected(joinGameData.playerName)) + { + server.SessionError(session, ERR_NET_PLAYER_NAME_IN_USE); + return retVal; + } + + // Create player data object. + boost::shared_ptr tmpPlayerData( + new PlayerData(m_curUniquePlayerId++, server.GetNextPlayerNumber(), joinGameData.ptype)); + tmpPlayerData->SetName(joinGameData.playerName); + tmpPlayerData->SetNetSessionData(session.sessionData); + + // Send ACK to client. + boost::shared_ptr answer(new NetPacketJoinGameAck); + NetPacketJoinGameAck::Data joinGameAckData; + joinGameAckData.sessionId = session.sessionData->GetId(); // TODO: currently unused. + joinGameAckData.yourPlayerUniqueId = tmpPlayerData->GetUniqueId(); + joinGameAckData.yourPlayerNum = tmpPlayerData->GetNumber(); + joinGameAckData.gameData = server.GetGameData(); + static_cast(answer.get())->SetData(joinGameAckData); + server.GetSender().Send(session.sessionData->GetSocket(), answer); + + // Send notifications for connected players to client. + PlayerDataList tmpPlayerList = server.GetPlayerDataList(); + PlayerDataList::iterator player_i = tmpPlayerList.begin(); + PlayerDataList::iterator player_end = tmpPlayerList.end(); + while (player_i != player_end) + { + boost::shared_ptr otherPlayerJoined(new NetPacketPlayerJoined); + NetPacketPlayerJoined::Data otherPlayerJoinedData; + otherPlayerJoinedData.playerId = (*player_i)->GetUniqueId(); + otherPlayerJoinedData.playerName = (*player_i)->GetName(); + otherPlayerJoinedData.playerNumber = (*player_i)->GetNumber(); + otherPlayerJoinedData.ptype = (*player_i)->GetType(); + static_cast(otherPlayerJoined.get())->SetData(otherPlayerJoinedData); + server.GetSender().Send(session.sessionData->GetSocket(), otherPlayerJoined); + + ++player_i; + } + + // Send "Player Joined" to other fully connected clients. + boost::shared_ptr thisPlayerJoined(new NetPacketPlayerJoined); + NetPacketPlayerJoined::Data thisPlayerJoinedData; + thisPlayerJoinedData.playerId = tmpPlayerData->GetUniqueId(); + thisPlayerJoinedData.playerName = tmpPlayerData->GetName(); + thisPlayerJoinedData.playerNumber = tmpPlayerData->GetNumber(); + thisPlayerJoinedData.ptype = tmpPlayerData->GetType(); + static_cast(thisPlayerJoined.get())->SetData(thisPlayerJoinedData); + server.SendToAllPlayers(thisPlayerJoined); + + // Set player data for session. + server.SetSessionPlayerData(session.sessionData, tmpPlayerData); + + // Session is now established. + session.sessionData->SetState(SessionData::Established); + return retVal; } @@ -463,62 +502,50 @@ ServerRecvStateWaitPlayerAction::~ServerRecvStateWaitPlayerAction() { } +void +ServerRecvStateWaitPlayerAction::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr connData) +{ + // C++ really sucks concerning multiple inheritance *sigh*. + // So this is a copy & paste from ServerRecvStateRunning. + + // Do not accept new connections in this state. + server.RejectNewConnection(connData); +} + int -ServerRecvStateWaitPlayerAction::Process(ServerRecvThread &server) +ServerRecvStateWaitPlayerAction::InternalProcess(ServerRecvThread &server, SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; - SOCKET recvSock = server.Select(); - if (recvSock != INVALID_SOCKET) + if (packet->ToNetPacketPlayersAction()) { - SessionWrapper session = server.GetSession(recvSock); - boost::shared_ptr packet; - try - { - packet = server.GetReceiver().Recv(recvSock); - } catch (const NetException &) - { - if (session.sessionData.get()) - { - server.CloseSessionDelayed(session); - return retVal; - } - } + NetPacketPlayersAction::Data actionData; + packet->ToNetPacketPlayersAction()->GetData(actionData); + + Game &curGame = server.GetGame(); + PlayerInterface *tmpPlayer = curGame.getPlayerByUniqueId(session.playerData->GetUniqueId()); + assert(tmpPlayer); // TODO throw exception - // Ignore if no session / no packet. - if (packet.get() && session.sessionData.get()) - { - if (packet->ToNetPacketPlayersAction()) - { - NetPacketPlayersAction::Data actionData; - packet->ToNetPacketPlayersAction()->GetData(actionData); - - Game &curGame = server.GetGame(); - PlayerInterface *tmpPlayer = curGame.getPlayerByUniqueId(session.playerData->GetUniqueId()); - assert(tmpPlayer); // TODO throw exception + tmpPlayer->setMyAction(actionData.playerAction); + tmpPlayer->setMySet(actionData.playerBet); - tmpPlayer->setMyAction(actionData.playerAction); - tmpPlayer->setMySet(actionData.playerBet); + if (tmpPlayer->getMySet() > GetHighestSet(curGame)) + SetHighestSet(curGame, tmpPlayer->getMySet()); - if (tmpPlayer->getMySet() > GetHighestSet(curGame)) - SetHighestSet(curGame, tmpPlayer->getMySet()); + boost::shared_ptr notifyActionDone(new NetPacketPlayersActionDone); + NetPacketPlayersActionDone::Data actionDoneData; + actionDoneData.gameState = static_cast(curGame.getCurrentHand()->getActualRound()); + actionDoneData.playerId = session.playerData->GetUniqueId(); + actionDoneData.playerAction = actionData.playerAction; + actionDoneData.totalPlayerBet = tmpPlayer->getMySet(); + actionDoneData.playerMoney = tmpPlayer->getMyCash(); + actionDoneData.potSize = curGame.getCurrentHand()->getBoard()->getPot(); + actionDoneData.curHandBets = curGame.getCurrentHand()->getBoard()->getSets(); + static_cast(notifyActionDone.get())->SetData(actionDoneData); + server.SendToAllPlayers(notifyActionDone); - boost::shared_ptr notifyActionDone(new NetPacketPlayersActionDone); - NetPacketPlayersActionDone::Data actionDoneData; - actionDoneData.gameState = static_cast(curGame.getCurrentHand()->getActualRound()); - actionDoneData.playerId = session.playerData->GetUniqueId(); - actionDoneData.playerAction = actionData.playerAction; - actionDoneData.totalPlayerBet = tmpPlayer->getMySet(); - actionDoneData.playerMoney = tmpPlayer->getMyCash(); - actionDoneData.potSize = curGame.getCurrentHand()->getBoard()->getPot(); - actionDoneData.curHandBets = curGame.getCurrentHand()->getBoard()->getSets(); - static_cast(notifyActionDone.get())->SetData(actionDoneData); - server.SendToAllPlayers(notifyActionDone); - - server.SetState(ServerRecvStateStartRound::Instance()); - retVal = MSG_NET_GAME_SERVER_ACTION; - } - } + server.SetState(ServerRecvStateStartRound::Instance()); + retVal = MSG_NET_GAME_SERVER_ACTION; } return retVal; diff --git a/src/net/netpacket.h b/src/net/netpacket.h index be71d722..bf28c7af 100644 --- a/src/net/netpacket.h +++ b/src/net/netpacket.h @@ -33,6 +33,7 @@ #define MAX_PACKET_SIZE 256 #define MAX_NAME_SIZE 64 #define MAX_PASSWORD_SIZE 64 +#define MAX_CHAT_TEXT_SIZE 128 struct NetPacketHeader; @@ -47,6 +48,8 @@ class NetPacketPlayersTurn; class NetPacketPlayersAction; class NetPacketPlayersActionDone; class NetPacketPlayersActionRejected; +class NetPacketSendChatText; +class NetPacketChatText; class NetPacketError; class NetPacket @@ -76,6 +79,8 @@ public: virtual const NetPacketPlayersAction *ToNetPacketPlayersAction() const; virtual const NetPacketPlayersActionDone *ToNetPacketPlayersActionDone() const; virtual const NetPacketPlayersActionRejected *ToNetPacketPlayersActionRejected() const; + virtual const NetPacketSendChatText *ToNetPacketSendChatText() const; + virtual const NetPacketChatText *ToNetPacketChatText() const; virtual const NetPacketError *ToNetPacketError() const; protected: @@ -343,6 +348,53 @@ protected: virtual void Check(const NetPacketHeader* data) const; }; +class NetPacketSendChatText : public NetPacket +{ +public: + struct Data + { + std::string text; + }; + + NetPacketSendChatText(); + virtual ~NetPacketSendChatText(); + + virtual boost::shared_ptr Clone() const; + + void SetData(const Data &inData); + void GetData(Data &outData) const; + + virtual const NetPacketSendChatText *ToNetPacketSendChatText() const; + +protected: + + virtual void Check(const NetPacketHeader* data) const; +}; + +class NetPacketChatText : public NetPacket +{ +public: + struct Data + { + u_int16_t playerId; + std::string text; + }; + + NetPacketChatText(); + virtual ~NetPacketChatText(); + + virtual boost::shared_ptr Clone() const; + + void SetData(const Data &inData); + void GetData(Data &outData) const; + + virtual const NetPacketChatText *ToNetPacketChatText() const; + +protected: + + virtual void Check(const NetPacketHeader* data) const; +}; + class NetPacketError : public NetPacket { public: diff --git a/src/net/serverrecvstate.h b/src/net/serverrecvstate.h index 4b5ed1f2..02c165a2 100644 --- a/src/net/serverrecvstate.h +++ b/src/net/serverrecvstate.h @@ -21,7 +21,7 @@ #ifndef _SERVERRECVSTATE_H_ #define _SERVERRECVSTATE_H_ -#include +#include #include #define SERVER_INITIAL_STATE ServerRecvStateInit @@ -29,7 +29,6 @@ class Game; class PlayerInterface; -class ServerRecvThread; class ServerCallback; class ServerRecvState @@ -44,8 +43,25 @@ public: virtual int Process(ServerRecvThread &server) = 0; }; +// Abstract State: Receiving. +class ServerRecvStateReceiving : public ServerRecvState +{ +public: + virtual ~ServerRecvStateReceiving(); + + // Globally handle packets which are allowed in all running states. + // Calls InternalProcess if packet has not been processed. + virtual int Process(ServerRecvThread &server); + +protected: + + ServerRecvStateReceiving(); + + virtual int InternalProcess(ServerRecvThread &server, SessionWrapper session, boost::shared_ptr packet) = 0; +}; + // State: Initialization. -class ServerRecvStateInit : public ServerRecvState +class ServerRecvStateInit : public ServerRecvStateReceiving { public: // Access the state singleton. @@ -56,14 +72,13 @@ public: // virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data); - // - virtual int Process(ServerRecvThread &server); - protected: // Protected constructor - this is a singleton. ServerRecvStateInit(); + virtual int InternalProcess(ServerRecvThread &server, SessionWrapper session, boost::shared_ptr packet); + private: unsigned m_curUniquePlayerId; @@ -75,10 +90,11 @@ class ServerRecvStateRunning : public ServerRecvState public: virtual ~ServerRecvStateRunning(); - // + // Reject new connections. virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data); protected: + ServerRecvStateRunning(); }; @@ -140,7 +156,7 @@ protected: }; // State: Wait for a player action. -class ServerRecvStateWaitPlayerAction : public ServerRecvStateRunning +class ServerRecvStateWaitPlayerAction : public ServerRecvStateReceiving { public: // Access the state singleton. @@ -148,14 +164,15 @@ public: virtual ~ServerRecvStateWaitPlayerAction(); - // - virtual int Process(ServerRecvThread &server); + virtual void HandleNewConnection(ServerRecvThread &server, boost::shared_ptr data); protected: // Protected constructor - this is a singleton. ServerRecvStateWaitPlayerAction(); + virtual int InternalProcess(ServerRecvThread &server, SessionWrapper session, boost::shared_ptr packet); + static int GetHighestSet(Game &curGame); static void SetHighestSet(Game &curGame, int highestSet); }; diff --git a/src/net/serverrecvthread.h b/src/net/serverrecvthread.h index d3324105..c34cb3fc 100644 --- a/src/net/serverrecvthread.h +++ b/src/net/serverrecvthread.h @@ -163,6 +163,7 @@ private: ConfigFile *m_playerConfig; friend class ServerRecvStateInit; +friend class ServerRecvStateReceiving; friend class ServerRecvStateRunning; friend class ServerRecvStateStartGame; friend class ServerRecvStateStartHand; diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index ab9b3e52..261dc567 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -48,6 +48,7 @@ #define ERR_NET_INVALID_PASSWORD_STR 105 #define ERR_NET_PLAYER_NAME_IN_USE 106 #define ERR_NET_INVALID_PLAYER_NAME 107 +#define ERR_NET_INVALID_CHAT_TEXT 108 // This is an internal message which is not reported. #define MSG_SOCK_INTERNAL_PENDING 0 diff --git a/src/session.cpp b/src/session.cpp index d9fd9743..7be8a4fc 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -185,12 +185,12 @@ void Session::terminateNetworkServer() void Session::sendClientPlayerAction() { if (!myNetClient) - return; + return; // only act if client is running. myNetClient->SendPlayerAction(); } -void Session::sendChatMessage(std::string message) { - - //hi lothar --> zum testen schick ich es gleich wieder zurück an die gui. wie du siehst klappt es ;-) - myGui->chatAppendMsg(message); +void Session::sendChatMessage(const std::string &message) { + if (!myNetClient) + return; // only act if client is running. + myNetClient->SendChatMessage(message); } diff --git a/src/session.h b/src/session.h index fac7e944..049c3d31 100755 --- a/src/session.h +++ b/src/session.h @@ -59,7 +59,7 @@ public: void setCurrentGameID(const int& theValue) { currentGameID = theValue; } int getCurrentGameID() const { return currentGameID; } - void sendChatMessage(std::string message); + void sendChatMessage(const std::string &message); private: