From da2939a7a8ade7e00ce7c1b2248e95ef0cd7fe11 Mon Sep 17 00:00:00 2001 From: lotodore Date: Sat, 28 Apr 2007 13:07:06 +0000 Subject: [PATCH] Error messages are shown when joining a network game fails. Still need to handle players which left and kicking players. --- docs/net_protocol.txt | 48 ++-- src/gui/qt/mainwindow/mainwindowimpl.cpp | 52 +++- src/net/common/clientstate.cpp | 34 ++- src/net/common/clientthread.cpp | 2 +- src/net/common/netpacket.cpp | 298 +++++++++++++---------- src/net/common/serverrecvstate.cpp | 146 ++++++----- src/net/common/serverrecvthread.cpp | 10 + src/net/common/sessiondata.cpp | 4 +- src/net/netpacket.h | 64 +++-- src/net/serverrecvthread.h | 1 + src/net/sessiondata.h | 4 +- src/net/socket_msg.h | 19 +- 12 files changed, 410 insertions(+), 272 deletions(-) diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index b965357e..29c0fa29 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -73,32 +73,13 @@ Player ID: Unique Player ID -Server Reply: Join Game Error - - 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 = 3 | Message Length = 8 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Error Reason | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Reason: - 0x01 - Version not supported - 0x02 - Server full - 0x03 - Game is already running - 0x04 - Invalid Password - 0x05 - Player Name already in use - 0x06 - Invalid Player Name - 0xFF - Other cause - Server Notification: Player Joined 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 = 4 | Message Length | + | Message Type = 3 | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Player ID | Player Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -116,7 +97,7 @@ Server Notification: Player Left 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 = 5 | Message Length = 8 | + | Message Type = 4 | Message Length = 8 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Player ID | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -127,7 +108,7 @@ Server Notification: Game Start 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 = 6 | Message Length = 4 | + | Message Type = 5 | Message Length = 4 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Your 1st Card | Your 2nd Card | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -141,11 +122,32 @@ Server Notification: Player's Turn 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 = 7 | Message Length = 8 | + | Message Type = 6 | Message Length = 8 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Player ID | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +Server Reply: Error + + 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 = 1024 | Message Length = 8 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Reason | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Reason: + 0x0001 - Join Game - Version not supported + 0x0002 - Join Game - Server full + 0x0003 - Join Game - Game is already running + 0x0004 - Join Game - Invalid Password + 0x0005 - Join Game - Player Name already in use + 0x0006 - Join Game - Invalid Player Name + 0xFF01 - General Error - Invalid packet + 0xFF02 - General Error - Invalid state + 0xFFFF - Other cause + 2007 by Lothar May diff --git a/src/gui/qt/mainwindow/mainwindowimpl.cpp b/src/gui/qt/mainwindow/mainwindowimpl.cpp index ec899d8d..ffa6011d 100755 --- a/src/gui/qt/mainwindow/mainwindowimpl.cpp +++ b/src/gui/qt/mainwindow/mainwindowimpl.cpp @@ -2191,8 +2191,58 @@ void mainWindowImpl::networkError(int errorID, int osErrorID) { tr("Connection was closed by server."), QMessageBox::Close); } break; + case ERR_SOCK_CONN_EXISTS: + { QMessageBox::warning(this, tr("Network Error"), + tr("Internal network error: Duplicate TCP connection."), + QMessageBox::Close); } + break; + case ERR_SOCK_INVALID_PACKET: + { QMessageBox::warning(this, tr("Network Error"), + tr("An invalid network packet was received.\nPlease make sure that all players use the same version of PokerTH."), + QMessageBox::Close); } + break; + case ERR_SOCK_INVALID_STATE: + { QMessageBox::warning(this, tr("Network Error"), + tr("Internal state error.\nPlease make sure that all players use the same version of PokerTH."), + QMessageBox::Close); } + break; + case ERR_NET_VERSION_NOT_SUPPORTED: + { QMessageBox::warning(this, tr("Network Error"), + tr("The PokerTH server does not support this version of the game.\nPlease update PokerTH."), + QMessageBox::Close); } + break; + case ERR_NET_SERVER_FULL: + { QMessageBox::warning(this, tr("Network Error"), + tr("Sorry, this server is already full."), + QMessageBox::Close); } + break; + case ERR_NET_GAME_ALREADY_RUNNING: + { QMessageBox::warning(this, tr("Network Error"), + tr("Unable to join - the server has already started the game."), + QMessageBox::Close); } + break; + case ERR_NET_INVALID_PASSWORD: + { QMessageBox::warning(this, tr("Network Error"), + tr("Invalid password when joining the game.\nPlease reenter the password and try again."), + QMessageBox::Close); } + break; + case ERR_NET_INVALID_PASSWORD_STR: + { QMessageBox::warning(this, tr("Network Error"), + tr("The password is too long. Please choose another one."), + QMessageBox::Close); } + break; + case ERR_NET_PLAYER_NAME_IN_USE: + { QMessageBox::warning(this, tr("Network Error"), + tr("Your player name is already used by another player.\nPlease choose a different name."), + QMessageBox::Close); } + break; + case ERR_NET_INVALID_PLAYER_NAME: + { QMessageBox::warning(this, tr("Network Error"), + tr("The player name is either too short or too long. Please choose another one."), + QMessageBox::Close); } + break; default: { QMessageBox::warning(this, tr("Network Error"), - tr("DEFAULT ERROR"), + tr("An internal error occured."), QMessageBox::Close); } } // close dialogs diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 803f43eb..07822725 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -357,26 +357,34 @@ ClientStateWaitSession::~ClientStateWaitSession() int ClientStateWaitSession::Process(ClientThread &client) { - int retVal; + 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() && tmpPacket->ToNetPacketJoinGameAck()) + if (tmpPacket.get()) { - // Initialise game configuration. - NetPacketJoinGameAck::Data joinGameAckData; - tmpPacket->ToNetPacketJoinGameAck()->GetData(joinGameAckData); - client.SetGameData(joinGameAckData.gameData); + 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.SetState(ClientStateWaitGame::Instance()); - retVal = MSG_SOCK_SESSION_DONE; - } - else // TODO: handle error packet - { - retVal = MSG_SOCK_INTERNAL_PENDING; + 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); + } } return retVal; @@ -412,7 +420,7 @@ ClientStateWaitGame::Process(ClientThread &client) if (tmpPacket.get() && tmpPacket->ToNetPacketGameStart()) { client.SetState(ClientStateFinal::Instance()); - retVal = MSG_SOCK_GAME_START; + retVal = MSG_NET_GAME_START; } else // TODO: handle error packet { diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 9e4c2687..8b7751dc 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -106,7 +106,7 @@ ClientThread::Main() GetCallback().SignalNetClientGameInfo(msg); // Additionally signal the start of the game. - if (msg == MSG_SOCK_GAME_START) + if (msg == MSG_NET_GAME_START) GetCallback().SignalNetClientGameStart(GetGameData()); } } diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index 8837aed8..94eaccc1 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -27,23 +27,25 @@ using namespace std; #define ADD_PADDING(x) ((((x) + 3) >> 2) << 2) -#define NET_TYPE_JOIN_GAME 1 -#define NET_TYPE_JOIN_GAME_ACK 2 -#define NET_TYPE_JOIN_GAME_ERROR 3 -#define NET_TYPE_PLAYER_JOINED 4 -#define NET_TYPE_PLAYER_LEFT 5 -#define NET_TYPE_GAME_START 6 +#define NET_TYPE_JOIN_GAME 0x0001 +#define NET_TYPE_JOIN_GAME_ACK 0x0002 +#define NET_TYPE_PLAYER_JOINED 0x0003 +#define NET_TYPE_PLAYER_LEFT 0x0004 +#define NET_TYPE_GAME_START 0x0005 -#define NET_PLAYER_FLAG_HUMAN 0x01 +#define NET_TYPE_ERROR 0x0400 -#define NET_JOIN_ERR_UNSUPPORTED_VERSION 0x01 -#define NET_JOIN_ERR_SERVER_FULL 0x02 -#define NET_JOIN_ERR_GAME_RUNNING 0x03 -#define NET_JOIN_ERR_INVALID_PASSWORD 0x04 -#define NET_JOIN_ERR_OTHER 0xFF +#define NET_PLAYER_FLAG_HUMAN 0x01 -#define NET_VERSION_MAJOR 1 -#define NET_VERSION_MINOR 0 +#define NET_ERR_JOIN_GAME_VERSION_NOT_SUPPORTED 0x0001 +#define NET_ERR_JOIN_GAME_SERVER_FULL 0x0002 +#define NET_ERR_JOIN_GAME_ALREADY_RUNNING 0x0003 +#define NET_ERR_JOIN_GAME_INVALID_PASSWORD 0x0004 +#define NET_ERR_JOIN_GAME_PLAYER_NAME_IN_USE 0x0005 +#define NET_ERR_JOIN_GAME_INVALID_PLAYER_NAME 0x0006 +#define NET_ERR_GENERAL_INVALID_PACKET 0xFF01 +#define NET_ERR_GENERAL_INVALID_STATE 0xFF02 +#define NET_ERR_OTHER 0xFFFF #ifdef _MSC_VER #pragma pack(push, 2) @@ -112,6 +114,13 @@ struct NetPacketGameStartData u_int16_t yourCards[2]; }; +struct NetPacketErrorData +{ + NetPacketHeader head; + u_int16_t reason; + u_int16_t reserved; +}; + #ifdef _MSC_VER #pragma pack(pop) #else @@ -149,6 +158,9 @@ NetPacket::Create(char *data, unsigned &dataSize) case NET_TYPE_GAME_START: tmpPacket = boost::shared_ptr(new NetPacketGameStart); break; + case NET_TYPE_ERROR: + tmpPacket = boost::shared_ptr(new NetPacketError); + break; } if (tmpPacket.get()) tmpPacket->SetRawData(tmpHeader); @@ -242,14 +254,14 @@ NetPacket::ToNetPacketJoinGameAck() const return NULL; } -const NetPacketJoinGameError * -NetPacket::ToNetPacketJoinGameError() const +const NetPacketGameStart * +NetPacket::ToNetPacketGameStart() const { return NULL; } -const NetPacketGameStart * -NetPacket::ToNetPacketGameStart() const +const NetPacketError * +NetPacket::ToNetPacketError() const { return NULL; } @@ -321,9 +333,9 @@ NetPacketJoinGame::SetData(const NetPacketJoinGame::Data &inData) u_int16_t passwordLen = (u_int16_t)inData.password.length(); if (!playerNameLen || playerNameLen > MAX_NAME_SIZE) - throw NetException(ERR_SOCK_INVALID_NAME_STR, 0); + throw NetException(ERR_NET_INVALID_PLAYER_NAME, 0); if (passwordLen > MAX_PASSWORD_SIZE) - throw NetException(ERR_SOCK_INVALID_PWD_STR, 0); + throw NetException(ERR_NET_INVALID_PASSWORD_STR, 0); // Resize the packet so that the data fits in. Resize((u_int16_t) @@ -347,6 +359,9 @@ NetPacketJoinGame::GetData(NetPacketJoinGame::Data &outData) const NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)GetRawData(); assert(tmpData); + outData.versionMajor = ntohs(tmpData->requestedVersionMajor); + outData.versionMinor = ntohs(tmpData->requestedVersionMinor); + outData.ptype = (ntohs(tmpData->playerFlags) & NET_PLAYER_FLAG_HUMAN) ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER; u_int16_t passwordLen = ntohs(tmpData->passwordLength); @@ -366,17 +381,26 @@ NetPacketJoinGame::Check(const NetPacketHeader* data) const assert(data); u_int16_t dataLen = ntohs(data->length); - if (dataLen < sizeof(NetPacketJoinGameData) - || dataLen > sizeof(NetPacketJoinGameData) + MAX_NAME_SIZE + MAX_PASSWORD_SIZE) + if (dataLen < sizeof(NetPacketJoinGameData)) { throw NetException(ERR_SOCK_INVALID_PACKET, 0); } NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)data; - if (dataLen != + int passwordLength = ntohs(tmpData->passwordLength); + int playerNameLength = ntohs(tmpData->playerNameLength); + // Generous checking - larger packets are allowed. + if (dataLen < sizeof(NetPacketJoinGameData) - + ADD_PADDING(ntohs(tmpData->passwordLength)) - + ADD_PADDING(ntohs(tmpData->playerNameLength))) + + ADD_PADDING(passwordLength) + + ADD_PADDING(playerNameLength)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + // Check string sizes. + if (passwordLength > MAX_PASSWORD_SIZE + || !playerNameLength + || playerNameLength > MAX_NAME_SIZE) { throw NetException(ERR_SOCK_INVALID_PACKET, 0); } @@ -458,107 +482,6 @@ NetPacketJoinGameAck::Check(const NetPacketHeader* data) const //----------------------------------------------------------------------------- -NetPacketJoinGameError::NetPacketJoinGameError() -: NetPacket(NET_TYPE_JOIN_GAME_ERROR, sizeof(NetPacketJoinGameErrorData)) -{ -} - -NetPacketJoinGameError::~NetPacketJoinGameError() -{ -} - -boost::shared_ptr -NetPacketJoinGameError::Clone() const -{ - boost::shared_ptr newPacket(new NetPacketJoinGameError); - try - { - newPacket->SetRawData(GetRawData()); - } catch (const NetException &) - { - // Need to return the new packet anyway. - } - return newPacket; -} - -void -NetPacketJoinGameError::SetData(const NetPacketJoinGameError::Data &inData) -{ - NetPacketJoinGameErrorData *tmpData = (NetPacketJoinGameErrorData *)GetRawData(); - assert(tmpData); - - switch (inData.reason) - { - case JOIN_UNSUPPORTED_VERSION : - tmpData->errorReason = htons(NET_JOIN_ERR_UNSUPPORTED_VERSION); - break; - case JOIN_SERVER_FULL : - tmpData->errorReason = htons(NET_JOIN_ERR_SERVER_FULL); - break; - case JOIN_GAME_RUNNING : - tmpData->errorReason = htons(NET_JOIN_ERR_GAME_RUNNING); - break; - case JOIN_INVALID_PASSWORD : - tmpData->errorReason = htons(NET_JOIN_ERR_INVALID_PASSWORD); - break; - default : - tmpData->errorReason = htons(NET_JOIN_ERR_OTHER); - break; - } -} - -void -NetPacketJoinGameError::GetData(NetPacketJoinGameError::Data &outData) const -{ - NetPacketJoinGameErrorData *tmpData = (NetPacketJoinGameErrorData *)GetRawData(); - assert(tmpData); - - switch (ntohs(tmpData->errorReason)) - { - case NET_JOIN_ERR_UNSUPPORTED_VERSION : - outData.reason = JOIN_UNSUPPORTED_VERSION; - break; - case NET_JOIN_ERR_SERVER_FULL : - outData.reason = JOIN_SERVER_FULL; - break; - case NET_JOIN_ERR_GAME_RUNNING : - outData.reason = JOIN_GAME_RUNNING; - break; - case NET_JOIN_ERR_INVALID_PASSWORD : - outData.reason = JOIN_INVALID_PASSWORD; - break; - default : - outData.reason = JOIN_UNKNOWN; - break; - } -} - -const NetPacketJoinGameError * -NetPacketJoinGameError::ToNetPacketJoinGameError() const -{ - return this; -} - -void -NetPacketJoinGameError::Check(const NetPacketHeader* data) const -{ - assert(data); - - u_int16_t dataLen = ntohs(data->length); - if (dataLen < sizeof(NetPacketJoinGameErrorData)) - { - throw NetException(ERR_SOCK_INVALID_PACKET, 0); - } - - NetPacketGameStartData *tmpData = (NetPacketGameStartData *)GetRawData(); - if (tmpData->yourCards[0] > 51 || tmpData->yourCards[1] > 51) - { - throw NetException(ERR_SOCK_INVALID_PACKET, 0); - } -} - -//----------------------------------------------------------------------------- - NetPacketGameStart::NetPacketGameStart() : NetPacket(NET_TYPE_GAME_START, sizeof(NetPacketGameStartData)) { @@ -628,3 +551,126 @@ NetPacketGameStart::Check(const NetPacketHeader* data) const //----------------------------------------------------------------------------- +NetPacketError::NetPacketError() +: NetPacket(NET_TYPE_ERROR, sizeof(NetPacketErrorData)) +{ +} + +NetPacketError::~NetPacketError() +{ +} + +boost::shared_ptr +NetPacketError::Clone() const +{ + boost::shared_ptr newPacket(new NetPacketError); + try + { + newPacket->SetRawData(GetRawData()); + } catch (const NetException &) + { + // Need to return the new packet anyway. + } + return newPacket; +} + +void +NetPacketError::SetData(const NetPacketError::Data &inData) +{ + NetPacketErrorData *tmpData = (NetPacketErrorData *)GetRawData(); + assert(tmpData); + + switch (inData.errorCode) + { + // Join Game Errors. + case ERR_NET_VERSION_NOT_SUPPORTED : + tmpData->reason = htons(NET_ERR_JOIN_GAME_VERSION_NOT_SUPPORTED); + break; + case ERR_NET_SERVER_FULL : + tmpData->reason = htons(NET_ERR_JOIN_GAME_SERVER_FULL); + break; + case ERR_NET_GAME_ALREADY_RUNNING : + tmpData->reason = htons(NET_ERR_JOIN_GAME_ALREADY_RUNNING); + break; + case ERR_NET_INVALID_PASSWORD : + tmpData->reason = htons(NET_ERR_JOIN_GAME_INVALID_PASSWORD); + break; + case ERR_NET_PLAYER_NAME_IN_USE : + tmpData->reason = htons(NET_ERR_JOIN_GAME_PLAYER_NAME_IN_USE); + break; + case ERR_NET_INVALID_PLAYER_NAME : + tmpData->reason = htons(NET_ERR_JOIN_GAME_INVALID_PLAYER_NAME); + break; + // General Errors. + case ERR_SOCK_INVALID_PACKET : + tmpData->reason = htons(NET_ERR_GENERAL_INVALID_PACKET); + break; + case ERR_SOCK_INVALID_STATE : + tmpData->reason = htons(NET_ERR_GENERAL_INVALID_STATE); + break; + default : + tmpData->reason = htons(NET_ERR_OTHER); + break; + } +} + +void +NetPacketError::GetData(NetPacketError::Data &outData) const +{ + NetPacketErrorData *tmpData = (NetPacketErrorData *)GetRawData(); + assert(tmpData); + + switch (ntohs(tmpData->reason)) + { + // Join Game Errors. + case NET_ERR_JOIN_GAME_VERSION_NOT_SUPPORTED : + outData.errorCode = ERR_NET_VERSION_NOT_SUPPORTED; + break; + case NET_ERR_JOIN_GAME_SERVER_FULL : + outData.errorCode = ERR_NET_SERVER_FULL; + break; + case NET_ERR_JOIN_GAME_ALREADY_RUNNING : + outData.errorCode = ERR_NET_GAME_ALREADY_RUNNING; + break; + case NET_ERR_JOIN_GAME_INVALID_PASSWORD : + outData.errorCode = ERR_NET_INVALID_PASSWORD; + break; + case NET_ERR_JOIN_GAME_PLAYER_NAME_IN_USE : + outData.errorCode = ERR_NET_PLAYER_NAME_IN_USE; + break; + case NET_ERR_JOIN_GAME_INVALID_PLAYER_NAME : + outData.errorCode = ERR_NET_INVALID_PLAYER_NAME; + break; + // General Errors. + case NET_ERR_GENERAL_INVALID_PACKET : + outData.errorCode = ERR_SOCK_INVALID_PACKET; + break; + case NET_ERR_GENERAL_INVALID_STATE : + outData.errorCode = ERR_SOCK_INVALID_STATE; + break; + default : + outData.errorCode = ERR_SOCK_INTERNAL; + break; + } +} + +const NetPacketError * +NetPacketError::ToNetPacketError() const +{ + return this; +} + +void +NetPacketError::Check(const NetPacketHeader* data) const +{ + assert(data); + + u_int16_t dataLen = ntohs(data->length); + if (dataLen < sizeof(NetPacketErrorData)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } +} + +//----------------------------------------------------------------------------- + diff --git a/src/net/common/serverrecvstate.cpp b/src/net/common/serverrecvstate.cpp index aea35a11..02f96b5f 100644 --- a/src/net/common/serverrecvstate.cpp +++ b/src/net/common/serverrecvstate.cpp @@ -23,8 +23,8 @@ #include #include #include +#include #include -#include using namespace std; @@ -56,13 +56,20 @@ ServerRecvStateInit::~ServerRecvStateInit() void ServerRecvStateInit::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr connData) { - boost::shared_ptr sessionData(new SessionData); + // Create a random session id. + // This id can be used to reconnect to the server if the connection was lost. + unsigned sessionId; + RandomBytes((unsigned char *)&sessionId, sizeof(sessionId)); // TODO: check for collisions. + + // Create a new session. + boost::shared_ptr sessionData(new SessionData(sessionId)); server.AddSession(connData, sessionData); } int ServerRecvStateInit::Process(ServerRecvThread &server) { + int retVal = MSG_SOCK_INIT_DONE; SOCKET recvSock = server.Select(); if (recvSock != INVALID_SOCKET) @@ -73,73 +80,86 @@ ServerRecvStateInit::Process(ServerRecvThread &server) // Ignore if no session / no packet. if (packet.get() && session.get()) { - if (session->GetState() == SessionData::Init) + // Session should be in initial state. + if (session->GetState() != SessionData::Init) { - // Only accept join game packets. - const NetPacketJoinGame *tmpPacket = packet->ToNetPacketJoinGame(); - if (tmpPacket) - { - NetPacketJoinGame::Data joinGameData; - tmpPacket->GetData(joinGameData); - // Check the server password. - if (server.CheckPassword(joinGameData.password)) - { - PlayerDataList &playerDataList = server.GetPlayerDataList(); - // Check whether this player is already connected. - PlayerDataList::const_iterator player_i = playerDataList.begin(); - PlayerDataList::const_iterator player_end = playerDataList.end(); - while (player_i != player_end) - { - if ((*player_i)->GetName() == joinGameData.playerName) - break; - ++player_i; - } - if (player_i == player_end) - { - // Create player data object. - boost::shared_ptr tmpPlayerData(new PlayerData(m_curUniquePlayerId++)); - tmpPlayerData->SetName(joinGameData.playerName); - tmpPlayerData->SetPlayerType(joinGameData.ptype); - - // Signal joining player to GUI. - server.GetCallback().SignalNetServerPlayerJoined(tmpPlayerData->GetName()); - - // Send ACK to client. - boost::shared_ptr answer(new NetPacketJoinGameAck); - NetPacketJoinGameAck::Data joinGameAckData; - joinGameAckData.playerId = tmpPlayerData->GetUniqueId(); - joinGameAckData.playerNumber = playerDataList.size(); - joinGameAckData.sessionId = session->GetId(); // TODO: currently unused. - joinGameAckData.gameData = server.GetGameData(); - static_cast(answer.get())->SetData(joinGameAckData); - server.GetSender().Send(answer, recvSock); - session->SetState(SessionData::Established); - - // Store player data in list. - playerDataList.push_back(tmpPlayerData); - } - else - { - // TODO send error message, duplicate name - } - } - else - { - // TODO send error message, invalid password - } - } - else - { - // TODO send error message, invalid packet - } + server.SendError(ERR_SOCK_INVALID_STATE, recvSock); + return retVal; } - else + + // Only accept join game packets. + const NetPacketJoinGame *tmpPacket = packet->ToNetPacketJoinGame(); + if (!tmpPacket) { - // TODO send error message, invalid state + server.SendError(ERR_SOCK_INVALID_PACKET, recvSock); + return retVal; } + + NetPacketJoinGame::Data joinGameData; + tmpPacket->GetData(joinGameData); + + // Check the protocol version. + if (joinGameData.versionMajor != NET_VERSION_MAJOR) + { + server.SendError(ERR_NET_VERSION_NOT_SUPPORTED, recvSock); + return retVal; + } + + // Check the server password. + if (!server.CheckPassword(joinGameData.password)) + { + server.SendError(ERR_NET_INVALID_PASSWORD, recvSock); + return retVal; + } + + PlayerDataList &playerDataList = server.GetPlayerDataList(); + + // Check the number of players. + if (playerDataList.size() >= (size_t)server.GetGameData().numberOfPlayers) + { + server.SendError(ERR_NET_SERVER_FULL, recvSock); + return retVal; + } + + // Check whether this player is already connected. + PlayerDataList::const_iterator player_i = playerDataList.begin(); + PlayerDataList::const_iterator player_end = playerDataList.end(); + while (player_i != player_end) + { + if ((*player_i)->GetName() == joinGameData.playerName) + break; + ++player_i; + } + if (player_i != player_end) + { + server.SendError(ERR_NET_PLAYER_NAME_IN_USE, recvSock); + return retVal; + } + + // Create player data object. + boost::shared_ptr tmpPlayerData(new PlayerData(m_curUniquePlayerId++)); + tmpPlayerData->SetName(joinGameData.playerName); + tmpPlayerData->SetPlayerType(joinGameData.ptype); + + // Signal joining player to GUI. + server.GetCallback().SignalNetServerPlayerJoined(tmpPlayerData->GetName()); + + // Send ACK to client. + boost::shared_ptr answer(new NetPacketJoinGameAck); + NetPacketJoinGameAck::Data joinGameAckData; + joinGameAckData.playerId = tmpPlayerData->GetUniqueId(); + joinGameAckData.playerNumber = playerDataList.size(); + joinGameAckData.sessionId = session->GetId(); // TODO: currently unused. + joinGameAckData.gameData = server.GetGameData(); + static_cast(answer.get())->SetData(joinGameAckData); + server.GetSender().Send(answer, recvSock); + session->SetState(SessionData::Established); + + // Store player data in list. + playerDataList.push_back(tmpPlayerData); } } - return MSG_SOCK_INIT_DONE; + return retVal; } //----------------------------------------------------------------------------- diff --git a/src/net/common/serverrecvthread.cpp b/src/net/common/serverrecvthread.cpp index bbb1ef72..0294db8a 100644 --- a/src/net/common/serverrecvthread.cpp +++ b/src/net/common/serverrecvthread.cpp @@ -66,6 +66,16 @@ ServerRecvThread::Init(const string &pwd, const GameData &gameData) *m_gameData = gameData; } +void +ServerRecvThread::SendError(int errorCode, SOCKET s) +{ + boost::shared_ptr packet(new NetPacketError); + NetPacketError::Data errorData; + errorData.errorCode = errorCode; + static_cast(packet.get())->SetData(errorData); + GetSender().Send(packet, s); +} + void ServerRecvThread::SendToAllPlayers(boost::shared_ptr packet) { diff --git a/src/net/common/sessiondata.cpp b/src/net/common/sessiondata.cpp index b0079f13..459b3abd 100644 --- a/src/net/common/sessiondata.cpp +++ b/src/net/common/sessiondata.cpp @@ -19,8 +19,8 @@ #include -SessionData::SessionData() -: m_id(SESSION_ID_INIT), m_state(SessionData::Init) +SessionData::SessionData(unsigned id) +: m_id(id), m_state(SessionData::Init) { } diff --git a/src/net/netpacket.h b/src/net/netpacket.h index e707134c..2bdd5c67 100644 --- a/src/net/netpacket.h +++ b/src/net/netpacket.h @@ -25,27 +25,21 @@ #include #include +#define NET_VERSION_MAJOR 1 +#define NET_VERSION_MINOR 0 + #define MIN_PACKET_SIZE 4 #define MAX_PACKET_SIZE 256 #define MAX_NAME_SIZE 64 #define MAX_PASSWORD_SIZE 64 -enum JoinGameErrorReason -{ - JOIN_UNSUPPORTED_VERSION, - JOIN_SERVER_FULL, - JOIN_GAME_RUNNING, - JOIN_INVALID_PASSWORD, - JOIN_UNKNOWN -}; - struct NetPacketHeader; class NetPacketJoinGame; class NetPacketJoinGameAck; -class NetPacketJoinGameError; class NetPacketGameStart; +class NetPacketError; class NetPacket { @@ -66,8 +60,8 @@ public: virtual const NetPacketJoinGame *ToNetPacketJoinGame() const; virtual const NetPacketJoinGameAck *ToNetPacketJoinGameAck() const; - virtual const NetPacketJoinGameError *ToNetPacketJoinGameError() const; virtual const NetPacketGameStart *ToNetPacketGameStart() const; + virtual const NetPacketError *ToNetPacketError() const; protected: @@ -85,6 +79,8 @@ class NetPacketJoinGame : public NetPacket public: struct Data { + int versionMajor; + int versionMinor; PlayerType ptype; std::string playerName; std::string password; @@ -131,29 +127,6 @@ protected: virtual void Check(const NetPacketHeader* data) const; }; -class NetPacketJoinGameError : public NetPacket -{ -public: - struct Data - { - JoinGameErrorReason reason; - }; - - NetPacketJoinGameError(); - virtual ~NetPacketJoinGameError(); - - virtual boost::shared_ptr Clone() const; - - void SetData(const Data &inData); - void GetData(Data &outData) const; - - virtual const NetPacketJoinGameError *ToNetPacketJoinGameError() const; - -protected: - - virtual void Check(const NetPacketHeader* data) const; -}; - class NetPacketGameStart : public NetPacket { public: @@ -177,5 +150,28 @@ protected: virtual void Check(const NetPacketHeader* data) const; }; +class NetPacketError : public NetPacket +{ +public: + struct Data + { + int errorCode; + }; + + NetPacketError(); + virtual ~NetPacketError(); + + virtual boost::shared_ptr Clone() const; + + void SetData(const Data &inData); + void GetData(Data &outData) const; + + virtual const NetPacketError *ToNetPacketError() const; + +protected: + + virtual void Check(const NetPacketHeader* data) const; +}; + #endif diff --git a/src/net/serverrecvthread.h b/src/net/serverrecvthread.h index fd021f12..c777533a 100644 --- a/src/net/serverrecvthread.h +++ b/src/net/serverrecvthread.h @@ -52,6 +52,7 @@ public: void Init(const std::string &pwd, const GameData &gameData); + void SendError(int errorCode, SOCKET s); void SendToAllPlayers(boost::shared_ptr packet); void AddConnection(boost::shared_ptr data); void AddNotification(unsigned notification); diff --git a/src/net/sessiondata.h b/src/net/sessiondata.h index 557e53c2..f7c157e4 100644 --- a/src/net/sessiondata.h +++ b/src/net/sessiondata.h @@ -30,13 +30,11 @@ class SessionData public: enum State { Init, Established }; - SessionData(); + SessionData(unsigned id); ~SessionData(); unsigned GetId() const {return m_id;} - void SetId(unsigned id) - {m_id = id;} State GetState() const {return m_state;} void SetState(State state) diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index f4bf7967..71ad2eb1 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -20,6 +20,7 @@ #ifndef _SOCKET_MSG_H_ #define _SOCKET_MSG_H_ +// Socket or socket related errors. #define ERR_SOCK_INTERNAL 1 #define ERR_SOCK_SERVERADDR_NOT_SET 2 #define ERR_SOCK_INVALID_PORT 3 @@ -36,9 +37,16 @@ #define ERR_SOCK_SEND_FAILED 14 #define ERR_SOCK_CONN_RESET 15 #define ERR_SOCK_CONN_EXISTS 16 -#define ERR_SOCK_INVALID_NAME_STR 17 -#define ERR_SOCK_INVALID_PWD_STR 18 -#define ERR_SOCK_INVALID_PACKET 19 +#define ERR_SOCK_INVALID_PACKET 17 +#define ERR_SOCK_INVALID_STATE 18 +// The following errors are game errors. +#define ERR_NET_VERSION_NOT_SUPPORTED 101 +#define ERR_NET_SERVER_FULL 102 +#define ERR_NET_GAME_ALREADY_RUNNING 103 +#define ERR_NET_INVALID_PASSWORD 104 +#define ERR_NET_INVALID_PASSWORD_STR 105 +#define ERR_NET_PLAYER_NAME_IN_USE 106 +#define ERR_NET_INVALID_PLAYER_NAME 107 // This is an internal message which is not reported. #define MSG_SOCK_INTERNAL_PENDING 0 @@ -50,11 +58,10 @@ #define MSG_SOCK_SESSION_DONE 4 #define MSG_SOCK_LIMIT_CONNECT MSG_SOCK_SESSION_DONE +#define MSG_SOCK_LAST MSG_SOCK_SESSION_DONE // The following messages are game messages. -#define MSG_SOCK_GAME_START 5 - -#define MSG_SOCK_LAST MSG_SOCK_SESSION_DONE +#define MSG_NET_GAME_START 5 #endif