diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index 1dbcb536..d2e9da9d 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -45,6 +45,9 @@ Additional values for major version = 1: [Future versions will include the name of the game, to support multiple games per server.] +Player Flags: + 0x01 set: Player is human + Server Reply: Join Game ACK @@ -83,7 +86,7 @@ Server Reply: Join Game Error Reason: 0x01 - Version not supported 0x02 - Server full - 0x03 - Game has already started + 0x03 - Game is already running 0x04 - Invalid Password 0xFF - Other cause @@ -95,7 +98,7 @@ Server Notification: Player Joined +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message Type = 4 | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Player ID | Player Number | + | Player ID | Player Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Player Flags | Name Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -105,10 +108,6 @@ Server Notification: Player Joined / | padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -Player Flags: - 0x01 set: Player is human - - Server Notification: Player Left diff --git a/src/gui/qt/mainwindow.ui b/src/gui/qt/mainwindow.ui index d1d79ad0..9d3e1567 100644 --- a/src/gui/qt/mainwindow.ui +++ b/src/gui/qt/mainwindow.ui @@ -4223,17 +4223,11 @@ Create Network Game ... - - false - Join Network Game ... - - false - diff --git a/src/net/clientcontext.h b/src/net/clientcontext.h index 829ae217..bfe1eaa4 100644 --- a/src/net/clientcontext.h +++ b/src/net/clientcontext.h @@ -55,6 +55,10 @@ public: {return &m_clientSockaddr;} sockaddr_storage *GetClientSockaddr() {return &m_clientSockaddr;} + const std::string &GetPlayerName() const + {return m_playerName;} + void SetPlayerName(const std::string &playerName) + {m_playerName = playerName;} int GetClientSockaddrSize() const {return m_addrFamily == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);} @@ -66,6 +70,7 @@ private: unsigned m_serverPort; std::string m_password; sockaddr_storage m_clientSockaddr; + std::string m_playerName; // TODO: use player interface. }; #endif diff --git a/src/net/clientthread.h b/src/net/clientthread.h index 4f72e7a5..4cd8b13f 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -41,7 +41,12 @@ public: // Set the parameters. Does not do any error checking. // Error checking will be done during connect // (i.e. after starting the thread). - void Init(const std::string &serverAddress, unsigned serverPort, bool ipv6, const std::string &pwd); + void Init( + const std::string &serverAddress, + unsigned serverPort, + bool ipv6, + const std::string &pwd, + const std::string &playerName); ClientCallback &GetCallback(); diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index fd02e372..435d699b 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -320,8 +320,17 @@ ClientStateStartSession::~ClientStateStartSession() int ClientStateStartSession::Process(ClientThread &client) { - boost::shared_ptr packet(new NetPacketInit(10)); - client.GetSender().Send(packet, client.GetContext().GetSocket()); + ClientContext &context = client.GetContext(); + + NetPacketJoinGame::Data initData; + initData.password = context.GetPassword(); + initData.playerName = context.GetPlayerName(); + initData.ptype = PLAYER_TYPE_HUMAN; // TODO + + boost::shared_ptr packet(new NetPacketJoinGame); + ((NetPacketJoinGame *)packet.get())->SetData(initData); + + client.GetSender().Send(packet, context.GetSocket()); client.SetState(ClientStateWaitSession::Instance()); @@ -355,7 +364,7 @@ ClientStateWaitSession::Process(ClientThread &client) boost::shared_ptr tmpPacket = client.GetReceiver().Recv(context.GetSocket()); - if (tmpPacket.get() && tmpPacket->ToNetPacketInitAck()) + if (tmpPacket.get() && tmpPacket->ToNetPacketJoinGameAck()) { client.SetState(ClientStateWaitGame::Instance()); retVal = MSG_SOCK_SESSION_DONE; diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 35e4176c..84e699a2 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -64,7 +64,8 @@ ClientThread::~ClientThread() } void -ClientThread::Init(const string &serverAddress, unsigned serverPort, bool ipv6, const string &pwd) +ClientThread::Init( + const string &serverAddress, unsigned serverPort, bool ipv6, const string &pwd, const string &playerName) { if (IsRunning()) return; // TODO: throw exception @@ -75,6 +76,7 @@ ClientThread::Init(const string &serverAddress, unsigned serverPort, bool ipv6, context.SetServerAddr(serverAddress); context.SetServerPort(serverPort); context.SetPassword(pwd); + context.SetPlayerName(playerName); } ClientCallback & diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index 748f398d..04da19e1 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -21,18 +21,229 @@ #include #include -NetPacket::~NetPacket() +#include + +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_PLAYER_FLAG_HUMAN 0x01 + +#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_VERSION_MAJOR 1 +#define NET_VERSION_MINOR 0 + +#ifdef _MSC_VER + #pragma pack(push, 2) +#else + #pragma align 2 +#endif + +struct NetPacketHeader { + u_int16_t type; + u_int16_t length; +}; + +struct NetPacketJoinGameData +{ + NetPacketHeader head; + u_int16_t requestedVersionMajor; + u_int16_t requestedVersionMinor; + u_int16_t passwordLength; + u_int16_t playerFlags; + u_int16_t playerNameLength; + u_int16_t reserved; + char password[1]; +}; + +struct NetPacketJoinGameAckData +{ + NetPacketHeader head; + u_int32_t sessionId; + u_int16_t playerId; + u_int16_t playerNumber; + u_int16_t numberOfPlayers; + u_int16_t smallBlind; + u_int16_t handsBeforeRaise; + u_int16_t gameSpeed; + u_int32_t startCash; +}; + +struct NetPacketJoinGameErrorData +{ + NetPacketHeader head; + u_int16_t errorReason; + u_int16_t reserved; +}; + +struct NetPacketPlayerJoinedData +{ + NetPacketHeader head; + u_int16_t playerId; + u_int16_t playerNumber; + u_int16_t playerFlags; + u_int16_t playerNameLength; + char playerName[1]; +}; + +struct NetPacketPlayerLeftData +{ + NetPacketHeader head; + u_int16_t playerId; + u_int16_t reserved; +}; + +struct NetPacketGameStartData +{ + NetPacketHeader head; + u_int16_t yourCards[2]; +}; + +#ifdef _MSC_VER + #pragma pack(pop) +#else + #pragma align 0 +#endif + + +boost::shared_ptr +NetPacket::Create(char *data, unsigned &dataSize) +{ + boost::shared_ptr tmpPacket; + + NetPacketHeader *tmpHeader = (NetPacketHeader *)data; + u_int16_t tmpLen = ntohs(tmpHeader->length); + + // Check size restrictions. + if (tmpLen < sizeof(NetPacketHeader) + || tmpLen > MAX_PACKET_SIZE) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + else if (dataSize >= tmpLen) + { + // OK - we have a complete packet. Construct a corresponding object. + try + { + switch(ntohs(tmpHeader->type)) + { + case NET_TYPE_JOIN_GAME: + tmpPacket = boost::shared_ptr(new NetPacketJoinGame); + break; + case NET_TYPE_JOIN_GAME_ACK: + tmpPacket = boost::shared_ptr(new NetPacketJoinGameAck); + break; + case NET_TYPE_GAME_START: + tmpPacket = boost::shared_ptr(new NetPacketGameStart); + break; + } + if (tmpPacket.get()) + tmpPacket->SetRawData(tmpHeader); + } catch (const NetException &) + { + tmpPacket.reset(); + } + + if (tmpLen < dataSize) + { + dataSize -= tmpLen; + memmove(data, data + tmpLen, dataSize); + } + else + dataSize = 0; + } + return tmpPacket; } -const NetPacketInit * -NetPacket::ToNetPacketInit() const +NetPacket::NetPacket(u_int16_t type, u_int16_t initialLen) +: m_data(NULL) +{ + assert(initialLen >= sizeof(NetPacketHeader)); + m_data = (NetPacketHeader *)malloc(initialLen); + assert(m_data); + memset(m_data, 0, initialLen); + m_data->type = htons(type); + m_data->length = htons(initialLen); +} + +NetPacket::~NetPacket() +{ + if (m_data) + free(m_data); +} + +const NetPacketHeader * +NetPacket::GetRawData() const +{ + assert(m_data); + return m_data; +} + +NetPacketHeader * +NetPacket::GetRawData() +{ + assert(m_data); + return m_data; +} + +void +NetPacket::SetRawData(const NetPacketHeader *p) +{ + if (!p) + return; + assert(m_data); + + u_int16_t tmpLen = ntohs(p->length); + if (ntohs(p->type) != GetType()) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + // Check whether the data is valid. + Check(p); + // Resize makes sure m_data is large enough. + Resize(tmpLen); + memcpy(m_data, p, tmpLen); +} + +u_int16_t +NetPacket::GetType() const +{ + return ntohs(GetRawData()->type); +} + +u_int16_t +NetPacket::GetLen() const +{ + return ntohs(GetRawData()->length); +} + +const NetPacketJoinGame * +NetPacket::ToNetPacketJoinGame() const { return NULL; } -const NetPacketInitAck * -NetPacket::ToNetPacketInitAck() const +const NetPacketJoinGameAck * +NetPacket::ToNetPacketJoinGameAck() const +{ + return NULL; +} + +const NetPacketJoinGameError * +NetPacket::ToNetPacketJoinGameError() const { return NULL; } @@ -43,38 +254,59 @@ NetPacket::ToNetPacketGameStart() const return NULL; } +void +NetPacket::Resize(u_int16_t newLen) +{ + assert(m_data); + u_int16_t oldLen = GetLen(); + if (newLen != oldLen) + { + if (newLen < sizeof(NetPacketHeader)) + throw NetException(ERR_SOCK_INTERNAL, 0); + else + { + NetPacketHeader *newData = (NetPacketHeader *)malloc(newLen); + if (!newData) + throw NetException(ERR_SOCK_INTERNAL, 0); + else + { + // Copy as much data as possible. + memcpy(newData, m_data, newLen > oldLen ? oldLen : newLen); + // Initialize new data to 0. + if (newLen > oldLen) + memset(((unsigned char *)newData) + oldLen, 0, newLen - oldLen); + // Set new len. + newData->length = htons(newLen); + // Switch over to new data. + free(m_data); + m_data = newData; + } + } + } +} + //----------------------------------------------------------------------------- -NetPacketInit::NetPacketInit() +NetPacketJoinGame::NetPacketJoinGame() +: NetPacket(NET_TYPE_JOIN_GAME, sizeof(NetPacketJoinGameData)) { - Init(); + NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)GetRawData(); + assert(tmpData); + tmpData->requestedVersionMajor = htons(NET_VERSION_MAJOR); + tmpData->requestedVersionMinor = htons(NET_VERSION_MINOR); } -NetPacketInit::NetPacketInit(u_int32_t value) +NetPacketJoinGame::~NetPacketJoinGame() { - Init(); - m_data.test = htonl(value); -} - -NetPacketInit::~NetPacketInit() -{ -} - -void -NetPacketInit::Init() -{ - m_data.head.type = htons(NET_TYPE_INIT); - m_data.head.length = htons(sizeof(m_data)); - m_data.test = htonl(0); } boost::shared_ptr -NetPacketInit::Clone() const +NetPacketJoinGame::Clone() const { - boost::shared_ptr newPacket(new NetPacketInit); + boost::shared_ptr newPacket(new NetPacketJoinGame); try { - newPacket->SetData(GetData()); + newPacket->SetRawData(GetRawData()); } catch (const NetException &) { // Need to return the new packet anyway. @@ -82,63 +314,92 @@ NetPacketInit::Clone() const return newPacket; } -const NetPacketHeader * -NetPacketInit::GetData() const +void +NetPacketJoinGame::SetData(const NetPacketJoinGame::Data &inData) { - return (const NetPacketHeader *)&m_data; + u_int16_t playerNameLen = (u_int16_t)inData.playerName.length(); + u_int16_t passwordLen = (u_int16_t)inData.password.length(); + + if (!playerNameLen || playerNameLen > MAX_NAME_SIZE) + throw NetException(ERR_SOCK_INVALID_NAME_STR, 0); + if (passwordLen > MAX_PASSWORD_SIZE) + throw NetException(ERR_SOCK_INVALID_PWD_STR, 0); + + // Resize the packet so that the data fits in. + Resize((u_int16_t) + (sizeof(NetPacketJoinGameData) + ADD_PADDING(playerNameLen) + ADD_PADDING(passwordLen))); + + NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)GetRawData(); + assert(tmpData); + + // Set the data. + 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); } void -NetPacketInit::SetData(const NetPacketHeader *p) +NetPacketJoinGame::GetData(NetPacketJoinGame::Data &outData) const { - u_int16_t tmpLen = ntohs(p->length); - if (tmpLen != sizeof(m_data) - || ntohs(p->type) != NET_TYPE_INIT) - { - throw NetException(ERR_SOCK_INTERNAL, 0); - } + // We assume that the data is valid. Validity has already been checked. + NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)GetRawData(); + assert(tmpData); - memcpy(&m_data, p, tmpLen); + 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)); } -const NetPacketInit * -NetPacketInit::ToNetPacketInit() const +const NetPacketJoinGame * +NetPacketJoinGame::ToNetPacketJoinGame() const { return this; } +void +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) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + + NetPacketJoinGameData *tmpData = (NetPacketJoinGameData *)data; + if (dataLen != + sizeof(NetPacketJoinGameData) + + ADD_PADDING(ntohs(tmpData->passwordLength)) + + ADD_PADDING(ntohs(tmpData->playerNameLength))) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } +} + //----------------------------------------------------------------------------- -NetPacketInitAck::NetPacketInitAck() -{ - Init(); -} - -NetPacketInitAck::NetPacketInitAck(u_int32_t value) -{ - Init(); - m_data.test = htonl(value); -} - -NetPacketInitAck::~NetPacketInitAck() +NetPacketJoinGameAck::NetPacketJoinGameAck() +: NetPacket(NET_TYPE_JOIN_GAME_ACK, sizeof(NetPacketJoinGameAckData)) { } -void -NetPacketInitAck::Init() +NetPacketJoinGameAck::~NetPacketJoinGameAck() { - m_data.head.type = htons(NET_TYPE_INIT_ACK); - m_data.head.length = htons(sizeof(m_data)); - m_data.test = htonl(0); } boost::shared_ptr -NetPacketInitAck::Clone() const +NetPacketJoinGameAck::Clone() const { - boost::shared_ptr newPacket(new NetPacketInitAck); + boost::shared_ptr newPacket(new NetPacketJoinGameAck); try { - newPacket->SetData(GetData()); + newPacket->SetRawData(GetRawData()); } catch (const NetException &) { // Need to return the new packet anyway. @@ -146,63 +407,176 @@ NetPacketInitAck::Clone() const return newPacket; } -const NetPacketHeader * -NetPacketInitAck::GetData() const +void +NetPacketJoinGameAck::SetData(const NetPacketJoinGameAck::Data &inData) { - return (const NetPacketHeader *)&m_data; + NetPacketJoinGameAckData *tmpData = (NetPacketJoinGameAckData *)GetRawData(); + assert(tmpData); + + tmpData->sessionId = htonl(inData.sessionId); + tmpData->playerId = htons(inData.playerId); + tmpData->playerNumber = htons(inData.playerNumber); + tmpData->numberOfPlayers = htons(inData.numberOfPlayers); + tmpData->smallBlind = htons(inData.smallBlind); + tmpData->handsBeforeRaise = htons(inData.handsBeforeRaise); + tmpData->gameSpeed = htons(inData.gameSpeed); + tmpData->startCash = htonl(inData.startCash); } void -NetPacketInitAck::SetData(const NetPacketHeader *p) +NetPacketJoinGameAck::GetData(NetPacketJoinGameAck::Data &outData) const { - 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); - } + NetPacketJoinGameAckData *tmpData = (NetPacketJoinGameAckData *)GetRawData(); + assert(tmpData); - memcpy(&m_data, p, tmpLen); + outData.sessionId = ntohl(tmpData->sessionId); + outData.playerId = ntohs(tmpData->playerId); + outData.playerNumber = ntohs(tmpData->playerNumber); + outData.numberOfPlayers = ntohs(tmpData->numberOfPlayers); + outData.smallBlind = ntohs(tmpData->smallBlind); + outData.handsBeforeRaise = ntohs(tmpData->handsBeforeRaise); + outData.gameSpeed = ntohs(tmpData->gameSpeed); + outData.startCash = ntohl(tmpData->startCash); } -const NetPacketInitAck * -NetPacketInitAck::ToNetPacketInitAck() const +const NetPacketJoinGameAck * +NetPacketJoinGameAck::ToNetPacketJoinGameAck() const { return this; } +void +NetPacketJoinGameAck::Check(const NetPacketHeader* data) const +{ + assert(data); + + u_int16_t dataLen = ntohs(data->length); + if (dataLen < sizeof(NetPacketJoinGameAckData)) + { + throw NetException(ERR_SOCK_INVALID_PACKET, 0); + } + // TODO: maybe some semantic checks +} + +//----------------------------------------------------------------------------- + +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)) { - 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); -} - boost::shared_ptr NetPacketGameStart::Clone() const { boost::shared_ptr newPacket(new NetPacketGameStart); try { - newPacket->SetData(GetData()); + newPacket->SetRawData(GetRawData()); } catch (const NetException &) { // Need to return the new packet anyway. @@ -210,23 +584,24 @@ NetPacketGameStart::Clone() const return newPacket; } -const NetPacketHeader * -NetPacketGameStart::GetData() const +void +NetPacketGameStart::SetData(const NetPacketGameStart::Data &inData) { - return (NetPacketHeader *)&m_data; + NetPacketGameStartData *tmpData = (NetPacketGameStartData *)GetRawData(); + assert(tmpData); + + tmpData->yourCards[0] = htons(inData.yourCards[0]); + tmpData->yourCards[1] = htons(inData.yourCards[1]); } void -NetPacketGameStart::SetData(const NetPacketHeader *p) +NetPacketGameStart::GetData(NetPacketGameStart::Data &outData) const { - 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); - } + NetPacketGameStartData *tmpData = (NetPacketGameStartData *)GetRawData(); + assert(tmpData); - memcpy(&m_data, p, tmpLen); + outData.yourCards[0] = ntohs(tmpData->yourCards[0]); + outData.yourCards[1] = ntohs(tmpData->yourCards[1]); } const NetPacketGameStart * @@ -235,3 +610,23 @@ NetPacketGameStart::ToNetPacketGameStart() const return this; } +void +NetPacketGameStart::Check(const NetPacketHeader* data) const +{ + assert(data); + + u_int16_t dataLen = ntohs(data->length); + if (dataLen < sizeof(NetPacketGameStartData)) + { + 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); + } +} + +//----------------------------------------------------------------------------- + diff --git a/src/net/common/receiverhelper.cpp b/src/net/common/receiverhelper.cpp index d9fd0d91..f321d8cf 100644 --- a/src/net/common/receiverhelper.cpp +++ b/src/net/common/receiverhelper.cpp @@ -98,50 +98,18 @@ ReceiverHelper::InternalGetPacket() // This is necessary, because we use TCP. // Packets may be received in multiple chunks or // several packets may be received at once. - if (m_tmpInBufSize >= sizeof(NetPacketHeader)) + if (m_tmpInBufSize >= MIN_PACKET_SIZE) { - NetPacketHeader *tmpHeader = (NetPacketHeader *)m_tmpInBuf; - u_int16_t tmpLen = ntohs(tmpHeader->length); - - if (tmpLen < sizeof(NetPacketHeader) - || tmpLen > MAX_PACKET_SIZE) + try { - // Invalid packet - reset input buffer. + // This call will also handle the memmove stuff, i.e. + // buffering for partial packets. + tmpPacket = NetPacket::Create(m_tmpInBuf, m_tmpInBufSize); + } catch (const NetException &) + { + // Reset buffer on error. m_tmpInBufSize = 0; } - else if (m_tmpInBufSize >= tmpLen) - { - tmpPacket = InternalCreateNetPacket(tmpHeader); - m_tmpInBufSize -= tmpLen; - } - } - return tmpPacket; -} - -boost::shared_ptr -ReceiverHelper::InternalCreateNetPacket(const NetPacketHeader *p) -{ - boost::shared_ptr tmpPacket; - - try - { - 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/senderthread.cpp b/src/net/common/senderthread.cpp index aa1ddd61..326245ab 100644 --- a/src/net/common/senderthread.cpp +++ b/src/net/common/senderthread.cpp @@ -71,11 +71,11 @@ SenderThread::Main() if (IS_VALID_SOCKET(tmpData.second)) m_curSocket = tmpData.second; - u_int16_t tmpLen = ntohs(tmpData.first->GetData()->length); + u_int16_t tmpLen = tmpData.first->GetLen(); if (tmpLen <= MAX_PACKET_SIZE) { m_tmpOutBufSize = tmpLen; - memcpy(m_tmpOutBuf, tmpData.first->GetData(), m_tmpOutBufSize); + memcpy(m_tmpOutBuf, tmpData.first->GetRawData(), tmpLen); } } } @@ -109,7 +109,7 @@ SenderThread::Main() } else if ((unsigned)bytesSent < m_tmpOutBufSize) { - m_tmpOutBufSize = m_tmpOutBufSize - (unsigned)bytesSent; + m_tmpOutBufSize -= (unsigned)bytesSent; memmove(m_tmpOutBuf, m_tmpOutBuf + bytesSent, m_tmpOutBufSize); } else diff --git a/src/net/common/serverrecvstate.cpp b/src/net/common/serverrecvstate.cpp index c13d712e..7091da6c 100644 --- a/src/net/common/serverrecvstate.cpp +++ b/src/net/common/serverrecvstate.cpp @@ -72,10 +72,13 @@ ServerRecvStateInit::Process(ServerRecvThread &server) { if (session->GetState() == SessionData::Init) { - // Only accept init packets. - if (packet->ToNetPacketInit()) + // Only accept join game packets. + const NetPacketJoinGame *tmpPacket = packet->ToNetPacketJoinGame(); + if (tmpPacket) { - boost::shared_ptr answer(new NetPacketInitAck); + // TODO: check password + // TODO: display name + boost::shared_ptr answer(new NetPacketJoinGameAck); server.GetSender().Send(answer, recvSock); session->SetState(SessionData::Established); } diff --git a/src/net/netpacket.h b/src/net/netpacket.h index 638e5267..85156c66 100644 --- a/src/net/netpacket.h +++ b/src/net/netpacket.h @@ -25,128 +25,166 @@ #include #include -#define MAX_PACKET_SIZE 256 +#define MIN_PACKET_SIZE 4 +#define MAX_PACKET_SIZE 256 +#define MAX_NAME_SIZE 64 +#define MAX_PASSWORD_SIZE 64 -#define NET_TYPE_INIT 0 -#define NET_TYPE_INIT_ACK 1 -#define NET_TYPE_GAME_START 2 - -#ifdef _MSC_VER - #pragma pack(push, 2) -#else - #pragma align 2 -#endif - -struct NetPacketHeader +// TODO: move this somewhere else +enum PlayerType { - u_int16_t type; - u_int16_t length; + PLAYER_TYPE_COMPUTER, + PLAYER_TYPE_HUMAN }; -struct NetPacketInitData +enum JoinGameErrorReason { - NetPacketHeader head; - u_int32_t test; + JOIN_UNSUPPORTED_VERSION, + JOIN_SERVER_FULL, + JOIN_GAME_RUNNING, + JOIN_INVALID_PASSWORD, + JOIN_UNKNOWN }; -struct NetPacketInitAckData -{ - NetPacketHeader head; - u_int32_t test; -}; +struct NetPacketHeader; -struct NetPacketGameStartData -{ - NetPacketHeader head; - u_int32_t test; -}; - -#ifdef _MSC_VER - #pragma pack(pop) -#else - #pragma align 0 -#endif - -class NetPacketInit; -class NetPacketInitAck; +class NetPacketJoinGame; +class NetPacketJoinGameAck; +class NetPacketJoinGameError; class NetPacketGameStart; class NetPacket { public: + static boost::shared_ptr Create(char *data, unsigned &dataSize); + + NetPacket(u_int16_t type, u_int16_t initialLen); virtual ~NetPacket(); virtual boost::shared_ptr Clone() const = 0; - virtual void SetData(const NetPacketHeader *p) = 0; - virtual const NetPacketHeader *GetData() const = 0; + const NetPacketHeader *GetRawData() const; + NetPacketHeader *GetRawData(); + void SetRawData(const NetPacketHeader *p); - virtual const NetPacketInit *ToNetPacketInit() const; - virtual const NetPacketInitAck *ToNetPacketInitAck() const; + u_int16_t GetType() const; + u_int16_t GetLen() const; + + virtual const NetPacketJoinGame *ToNetPacketJoinGame() const; + virtual const NetPacketJoinGameAck *ToNetPacketJoinGameAck() const; + virtual const NetPacketJoinGameError *ToNetPacketJoinGameError() const; virtual const NetPacketGameStart *ToNetPacketGameStart() const; + +protected: + + virtual void Check(const NetPacketHeader* data) const = 0; + + void Resize(u_int16_t newLen); + +private: + + NetPacketHeader *m_data; }; -class NetPacketInit : public NetPacket +class NetPacketJoinGame : public NetPacket { public: - NetPacketInit(); - NetPacketInit(u_int32_t value); - virtual ~NetPacketInit(); + struct Data + { + PlayerType ptype; + std::string playerName; + std::string password; + }; + + NetPacketJoinGame(); + virtual ~NetPacketJoinGame(); virtual boost::shared_ptr Clone() const; - virtual const NetPacketHeader *GetData() const; - virtual void SetData(const NetPacketHeader *p); + void SetData(const Data &inData); + void GetData(Data &outData) const; - virtual const NetPacketInit *ToNetPacketInit() const; + virtual const NetPacketJoinGame *ToNetPacketJoinGame() const; protected: - void Init(); -private: - NetPacketInitData m_data; + virtual void Check(const NetPacketHeader* data) const; }; -class NetPacketInitAck : public NetPacket +class NetPacketJoinGameAck : public NetPacket { public: - NetPacketInitAck(); - NetPacketInitAck(u_int32_t value); - virtual ~NetPacketInitAck(); + struct Data + { + u_int32_t sessionId; + u_int16_t playerId; + u_int16_t playerNumber; + u_int16_t numberOfPlayers; + u_int16_t smallBlind; + u_int16_t handsBeforeRaise; + u_int16_t gameSpeed; + u_int32_t startCash; + }; + + NetPacketJoinGameAck(); + virtual ~NetPacketJoinGameAck(); virtual boost::shared_ptr Clone() const; - virtual const NetPacketHeader *GetData() const; - virtual void SetData(const NetPacketHeader *p); + void SetData(const Data &inData); + void GetData(Data &outData) const; - virtual const NetPacketInitAck *ToNetPacketInitAck() const; + virtual const NetPacketJoinGameAck *ToNetPacketJoinGameAck() const; protected: - void Init(); -private: - NetPacketInitAckData m_data; + 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: + struct Data + { + u_int16_t yourCards[2]; + }; + NetPacketGameStart(); - NetPacketGameStart(u_int32_t value); virtual ~NetPacketGameStart(); virtual boost::shared_ptr Clone() const; - virtual const NetPacketHeader *GetData() const; - virtual void SetData(const NetPacketHeader *p); + void SetData(const Data &inData); + void GetData(Data &outData) const; virtual const NetPacketGameStart *ToNetPacketGameStart() const; protected: - void Init(); -private: - NetPacketGameStartData m_data; + virtual void Check(const NetPacketHeader* data) const; }; #endif diff --git a/src/net/receiverhelper.h b/src/net/receiverhelper.h index 82090012..cddeb813 100644 --- a/src/net/receiverhelper.h +++ b/src/net/receiverhelper.h @@ -45,7 +45,6 @@ public: protected: boost::shared_ptr InternalGetPacket(); - boost::shared_ptr InternalCreateNetPacket(const NetPacketHeader *p); private: diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index abcc1cf1..f4bf7967 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -36,6 +36,9 @@ #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 // 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 34678357..099cb23c 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -64,7 +64,12 @@ void Session::startNetworkClient(const string &serverAddress, unsigned serverPor if (myNetClient || !myGui) return; // TODO: throw exception myNetClient = new ClientThread(*myGui); - myNetClient->Init(serverAddress, serverPort, ipv6, pwd); + myNetClient->Init( + serverAddress, + serverPort, + ipv6, + pwd, + myConfig->readConfigString("MyName")); myNetClient->Run(); } @@ -77,7 +82,8 @@ void Session::startNetworkClientForLocalServer() "localhost", myConfig->readConfigInt("ServerPort"), myConfig->readConfigInt("ServerUseIpv6") == 1, - myConfig->readConfigString("ServerPassword")); + myConfig->readConfigString("ServerPassword"), + myConfig->readConfigString("MyName")); myNetClient->Run(); }