From 31ac6e7fdfda9e62548e527cc0e8dcaaef10fcfb Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 28 May 2016 17:54:45 +0100 Subject: [PATCH] refactor NetworkConnection and NetworkPacket --- openrct2.vcxproj | 4 + src/network/NetworkConnection.cpp | 221 +++++++++++++++++++++++ src/network/NetworkConnection.h | 62 +++++++ src/network/NetworkPacket.cpp | 114 ++++++++++++ src/network/NetworkPacket.h | 70 ++++++++ src/network/NetworkTypes.h | 113 ++++++++++++ src/network/network.cpp | 280 ------------------------------ src/network/network.h | 126 +------------- 8 files changed, 587 insertions(+), 403 deletions(-) create mode 100644 src/network/NetworkConnection.cpp create mode 100644 src/network/NetworkConnection.h create mode 100644 src/network/NetworkPacket.cpp create mode 100644 src/network/NetworkPacket.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 4d4d928b7e..8ead1c7d52 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -84,7 +84,9 @@ + + @@ -360,6 +362,8 @@ + + diff --git a/src/network/NetworkConnection.cpp b/src/network/NetworkConnection.cpp new file mode 100644 index 0000000000..5b40239e40 --- /dev/null +++ b/src/network/NetworkConnection.cpp @@ -0,0 +1,221 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "NetworkConnection.h" +#include "../core/String.hpp" +#include + +extern "C" +{ + #include "../localisation/localisation.h" +} + +constexpr size_t NETWORK_DISCONNECT_REASON_BUFFER_SIZE = 256; + +NetworkConnection::NetworkConnection() +{ + authstatus = NETWORK_AUTH_NONE; + player = 0; + socket = INVALID_SOCKET; + ResetLastPacketTime(); + last_disconnect_reason = nullptr; +} + +NetworkConnection::~NetworkConnection() +{ + if (socket != INVALID_SOCKET) + { + closesocket(socket); + } + if (last_disconnect_reason) + { + delete[] last_disconnect_reason; + } +} + +int NetworkConnection::ReadPacket() +{ + if (inboundpacket.transferred < sizeof(inboundpacket.size)) + { + // read packet size + int readBytes = recv(socket, &((char*)&inboundpacket.size)[inboundpacket.transferred], sizeof(inboundpacket.size) - inboundpacket.transferred, 0); + if (readBytes == SOCKET_ERROR || readBytes == 0) + { + if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) + { + return NETWORK_READPACKET_DISCONNECTED; + } + else + { + return NETWORK_READPACKET_NO_DATA; + } + } + inboundpacket.transferred += readBytes; + if (inboundpacket.transferred == sizeof(inboundpacket.size)) + { + inboundpacket.size = ntohs(inboundpacket.size); + if (inboundpacket.size == 0) // Can't have a size 0 packet + { + return NETWORK_READPACKET_DISCONNECTED; + } + inboundpacket.data->resize(inboundpacket.size); + } + } + else + { + // read packet data + if (inboundpacket.data->capacity() > 0) + { + int readBytes = recv(socket, + (char*)&inboundpacket.GetData()[inboundpacket.transferred - sizeof(inboundpacket.size)], + sizeof(inboundpacket.size) + inboundpacket.size - inboundpacket.transferred, + 0); + if (readBytes == SOCKET_ERROR || readBytes == 0) + { + if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) + { + return NETWORK_READPACKET_DISCONNECTED; + } + else + { + return NETWORK_READPACKET_NO_DATA; + } + } + inboundpacket.transferred += readBytes; + } + if (inboundpacket.transferred == sizeof(inboundpacket.size) + inboundpacket.size) + { + last_packet_time = SDL_GetTicks(); + return NETWORK_READPACKET_SUCCESS; + } + } + return NETWORK_READPACKET_MORE_DATA; +} + +bool NetworkConnection::SendPacket(NetworkPacket& packet) +{ + uint16 sizen = htons(packet.size); + std::vector tosend; + tosend.reserve(sizeof(sizen) + packet.size); + tosend.insert(tosend.end(), (uint8*)&sizen, (uint8*)&sizen + sizeof(sizen)); + tosend.insert(tosend.end(), packet.data->begin(), packet.data->end()); + while (true) + { + int sentBytes = send(socket, (const char*)&tosend[packet.transferred], tosend.size() - packet.transferred, FLAG_NO_PIPE); + if (sentBytes == SOCKET_ERROR) + { + return false; + } + packet.transferred += sentBytes; + if (packet.transferred == tosend.size()) + { + return true; + } + } + return false; +} + +void NetworkConnection::QueuePacket(std::unique_ptr packet, bool front) +{ + if (authstatus == NETWORK_AUTH_OK || !packet->CommandRequiresAuth()) + { + packet->size = (uint16)packet->data->size(); + if (front) + { + outboundpackets.push_front(std::move(packet)); + } + else + { + outboundpackets.push_back(std::move(packet)); + } + } +} + +void NetworkConnection::SendQueuedPackets() +{ + while (outboundpackets.size() > 0 && SendPacket(*(outboundpackets.front()).get())) + { + outboundpackets.remove(outboundpackets.front()); + } +} + +bool NetworkConnection::SetTCPNoDelay(bool on) +{ + return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on)) == 0; +} + +bool NetworkConnection::SetNonBlocking(bool on) +{ + return SetNonBlocking(socket, on); +} + +bool NetworkConnection::SetNonBlocking(SOCKET socket, bool on) +{ +#ifdef __WINDOWS__ + u_long nonblocking = on; + return ioctlsocket(socket, FIONBIO, &nonblocking) == 0; +#else + int flags = fcntl(socket, F_GETFL, 0); + return fcntl(socket, F_SETFL, on ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)) == 0; +#endif +} + +void NetworkConnection::ResetLastPacketTime() +{ + last_packet_time = SDL_GetTicks(); +} + +bool NetworkConnection::ReceivedPacketRecently() +{ +#ifndef DEBUG + if (SDL_TICKS_PASSED(SDL_GetTicks(), last_packet_time + 7000)) + { + return false; + } +#endif + return true; +} + +const utf8 * NetworkConnection::getLastDisconnectReason() const +{ + return this->last_disconnect_reason; +} + +void NetworkConnection::setLastDisconnectReason(const utf8 * src) +{ + if (src == nullptr) + { + if (last_disconnect_reason) + { + delete[] last_disconnect_reason; + last_disconnect_reason = nullptr; + } + return; + } + + if (last_disconnect_reason == nullptr) + { + last_disconnect_reason = new utf8[NETWORK_DISCONNECT_REASON_BUFFER_SIZE]; + } + String::Set(last_disconnect_reason, NETWORK_DISCONNECT_REASON_BUFFER_SIZE, src); +} + +void NetworkConnection::setLastDisconnectReason(const rct_string_id string_id, void *args) +{ + char buffer[NETWORK_DISCONNECT_REASON_BUFFER_SIZE]; + format_string(buffer, string_id, args); + setLastDisconnectReason(buffer); +} diff --git a/src/network/NetworkConnection.h b/src/network/NetworkConnection.h new file mode 100644 index 0000000000..f1a7f927bd --- /dev/null +++ b/src/network/NetworkConnection.h @@ -0,0 +1,62 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include +#include +#include +#include "NetworkTypes.h" +#include "NetworkKey.h" +#include "NetworkPacket.h" +#include "../common.h" + +class NetworkPlayer; + +class NetworkConnection +{ +public: + SOCKET socket = INVALID_SOCKET; + NetworkPacket inboundpacket; + NETWORK_AUTH authstatus = NETWORK_AUTH_NONE; + NetworkPlayer * player; + uint32 ping_time = 0; + NetworkKey key; + std::vector challenge; + + NetworkConnection(); + ~NetworkConnection(); + + int ReadPacket(); + void QueuePacket(std::unique_ptr packet, bool front = false); + void SendQueuedPackets(); + bool SetTCPNoDelay(bool on); + bool SetNonBlocking(bool on); + static bool SetNonBlocking(SOCKET socket, bool on); + void ResetLastPacketTime(); + bool ReceivedPacketRecently(); + + const utf8 * getLastDisconnectReason() const; + void setLastDisconnectReason(const utf8 * src); + void setLastDisconnectReason(const rct_string_id string_id, void * args = nullptr); + +private: + utf8 * last_disconnect_reason; + + bool SendPacket(NetworkPacket &packet); + std::list> outboundpackets; + uint32 last_packet_time; +}; diff --git a/src/network/NetworkPacket.cpp b/src/network/NetworkPacket.cpp new file mode 100644 index 0000000000..52645e5b0a --- /dev/null +++ b/src/network/NetworkPacket.cpp @@ -0,0 +1,114 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "NetworkTypes.h" +#include "NetworkPacket.h" + +NetworkPacket::NetworkPacket() +{ + transferred = 0; + read = 0; + size = 0; + data = std::make_shared>(); +} + +std::unique_ptr NetworkPacket::Allocate() +{ + return std::unique_ptr(new NetworkPacket); // change to make_unique in c++14 +} + +std::unique_ptr NetworkPacket::Duplicate(NetworkPacket &packet) +{ + return std::unique_ptr(new NetworkPacket(packet)); // change to make_unique in c++14 +} + +uint8 * NetworkPacket::GetData() +{ + return &(*data)[0]; +} + +uint32 NetworkPacket::GetCommand() +{ + if (data->size() >= sizeof(uint32)) + { + return ByteSwapBE(*(uint32 *)(&(*data)[0])); + } + else + { + return NETWORK_COMMAND_INVALID; + } +} + +void NetworkPacket::Clear() +{ + transferred = 0; + read = 0; + data->clear(); +} + +bool NetworkPacket::CommandRequiresAuth() +{ + switch (GetCommand()) { + case NETWORK_COMMAND_PING: + case NETWORK_COMMAND_AUTH: + case NETWORK_COMMAND_TOKEN: + case NETWORK_COMMAND_GAMEINFO: + return false; + default: + return true; + } +} + +void NetworkPacket::Write(const uint8 * bytes, uint32 size) +{ + data->insert(data->end(), bytes, bytes + size); +} + +void NetworkPacket::WriteString(const utf8 * string) +{ + Write((uint8 *)string, strlen(string) + 1); +} + +const uint8 * NetworkPacket::Read(uint32 size) +{ + if (read + size > NetworkPacket::size) + { + return nullptr; + } + else + { + uint8 * data = &GetData()[read]; + read += size; + return data; + } +} + +const utf8 * NetworkPacket::ReadString() +{ + char * str = (char *)&GetData()[read]; + char * strend = str; + while (read < size && *strend != 0) + { + read++; + strend++; + } + if (*strend != 0) + { + return nullptr; + } + read++; + return str; +} diff --git a/src/network/NetworkPacket.h b/src/network/NetworkPacket.h new file mode 100644 index 0000000000..290a4c4b96 --- /dev/null +++ b/src/network/NetworkPacket.h @@ -0,0 +1,70 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include +#include +#include "../common.h" + +class NetworkPacket +{ +public: + uint16 size; + std::shared_ptr> data; + uint32 transferred; + sint32 read; + + static std::unique_ptr Allocate(); + static std::unique_ptr Duplicate(NetworkPacket& packet); + + NetworkPacket(); + + uint8 * GetData(); + uint32 GetCommand(); + + void Clear(); + bool CommandRequiresAuth(); + + const uint8 * Read(uint32 size); + const utf8 * ReadString(); + + void Write(const uint8 * bytes, uint32 size); + void WriteString(const utf8 * string); + + template + NetworkPacket & operator >>(T &value) + { + if (read + sizeof(value) > size) + { + value = 0; + } + else + { + value = ByteSwapBE(*((T *)&GetData()[read])); + read += sizeof(value); + } + return *this; + } + + template + NetworkPacket & operator <<(T value) { + T swapped = ByteSwapBE(value); + uint8 * bytes = (uint8 *)&swapped; + data->insert(data->end(), bytes, bytes + sizeof(value)); + return *this; + } +}; diff --git a/src/network/NetworkTypes.h b/src/network/NetworkTypes.h index d11cbd5b27..47239aad41 100644 --- a/src/network/NetworkTypes.h +++ b/src/network/NetworkTypes.h @@ -16,12 +16,125 @@ #pragma once +#include #include #ifdef __WINDOWS__ // winsock2 must be included before windows.h #include #include + + #define LAST_SOCKET_ERROR() WSAGetLastError() + #undef EWOULDBLOCK + #define EWOULDBLOCK WSAEWOULDBLOCK + #ifndef SHUT_RD + #define SHUT_RD SD_RECEIVE + #endif + #ifndef SHUT_RDWR + #define SHUT_RDWR SD_BOTH + #endif + #define FLAG_NO_PIPE 0 #else + #include + #include + #include + #include #include + #include + typedef int SOCKET; + #define SOCKET_ERROR -1 + #define INVALID_SOCKET -1 + #define LAST_SOCKET_ERROR() errno + #define closesocket close + #define ioctlsocket ioctl + #if defined(__LINUX__) + #define FLAG_NO_PIPE MSG_NOSIGNAL + #else + #define FLAG_NO_PIPE 0 + #endif // defined(__LINUX__) #endif // __WINDOWS__ + +#include "../common.h" + +enum NETWORK_READPACKET +{ + NETWORK_READPACKET_SUCCESS, + NETWORK_READPACKET_NO_DATA, + NETWORK_READPACKET_MORE_DATA, + NETWORK_READPACKET_DISCONNECTED +}; + +enum NETWORK_AUTH +{ + NETWORK_AUTH_NONE, + NETWORK_AUTH_REQUESTED, + NETWORK_AUTH_OK, + NETWORK_AUTH_BADVERSION, + NETWORK_AUTH_BADNAME, + NETWORK_AUTH_BADPASSWORD, + NETWORK_AUTH_VERIFICATIONFAILURE, + NETWORK_AUTH_FULL, + NETWORK_AUTH_REQUIREPASSWORD, + NETWORK_AUTH_VERIFIED, + NETWORK_AUTH_UNKNOWN_KEY_DISALLOWED, +}; + +enum NETWORK_COMMAND +{ + NETWORK_COMMAND_AUTH, + NETWORK_COMMAND_MAP, + NETWORK_COMMAND_CHAT, + NETWORK_COMMAND_GAMECMD, + NETWORK_COMMAND_TICK, + NETWORK_COMMAND_PLAYERLIST, + NETWORK_COMMAND_PING, + NETWORK_COMMAND_PINGLIST, + NETWORK_COMMAND_SETDISCONNECTMSG, + NETWORK_COMMAND_GAMEINFO, + NETWORK_COMMAND_SHOWERROR, + NETWORK_COMMAND_GROUPLIST, + NETWORK_COMMAND_EVENT, + NETWORK_COMMAND_TOKEN, + NETWORK_COMMAND_MAX, + NETWORK_COMMAND_INVALID = -1 +}; + +#ifdef __cplusplus + +template +struct ByteSwapT { }; + +template <> +struct ByteSwapT<1> +{ + static uint8 SwapBE(uint8 value) + { + return value; + } +}; + +template <> +struct ByteSwapT<2> +{ + static uint16 SwapBE(uint16 value) + { + return SDL_SwapBE16(value); + } +}; + +template <> +struct ByteSwapT<4> +{ + static uint32 SwapBE(uint32 value) + { + return SDL_SwapBE32(value); + } +}; + +template +T ByteSwapBE(const T& value) +{ + return ByteSwapT::SwapBE(value); +} + +#endif diff --git a/src/network/network.cpp b/src/network/network.cpp index 51b64cd0ae..2133243f56 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -62,32 +62,6 @@ extern "C" { Network gNetwork; NetworkActions gNetworkActions; -enum { - NETWORK_READPACKET_SUCCESS, - NETWORK_READPACKET_NO_DATA, - NETWORK_READPACKET_MORE_DATA, - NETWORK_READPACKET_DISCONNECTED -}; - -enum { - NETWORK_COMMAND_AUTH, - NETWORK_COMMAND_MAP, - NETWORK_COMMAND_CHAT, - NETWORK_COMMAND_GAMECMD, - NETWORK_COMMAND_TICK, - NETWORK_COMMAND_PLAYERLIST, - NETWORK_COMMAND_PING, - NETWORK_COMMAND_PINGLIST, - NETWORK_COMMAND_SETDISCONNECTMSG, - NETWORK_COMMAND_GAMEINFO, - NETWORK_COMMAND_SHOWERROR, - NETWORK_COMMAND_GROUPLIST, - NETWORK_COMMAND_EVENT, - NETWORK_COMMAND_TOKEN, - NETWORK_COMMAND_MAX, - NETWORK_COMMAND_INVALID = -1 -}; - enum { ADVERTISE_STATUS_DISABLED, ADVERTISE_STATUS_UNREGISTERED, @@ -115,94 +89,6 @@ static void network_get_private_key_path(utf8 *buffer, size_t bufferSize, const static void network_get_public_key_path(utf8 *buffer, size_t bufferSize, const utf8 * playerName, const utf8 * hash); static void network_get_keymap_path(utf8 *buffer, size_t bufferSize); -NetworkPacket::NetworkPacket() -{ - transferred = 0; - read = 0; - size = 0; - data = std::make_shared>(); -} - -std::unique_ptr NetworkPacket::Allocate() -{ - return std::unique_ptr(new NetworkPacket); // change to make_unique in c++14 -} - -std::unique_ptr NetworkPacket::Duplicate(NetworkPacket& packet) -{ - return std::unique_ptr(new NetworkPacket(packet)); // change to make_unique in c++14 -} - -uint8* NetworkPacket::GetData() -{ - return &(*data)[0]; -} - -uint32 NetworkPacket::GetCommand() -{ - if (data->size() >= sizeof(uint32)) { - return ByteSwapBE(*(uint32*)(&(*data)[0])); - } else { - return NETWORK_COMMAND_INVALID; - } -} - -void NetworkPacket::Write(const uint8* bytes, unsigned int size) -{ - data->insert(data->end(), bytes, bytes + size); -} - -void NetworkPacket::WriteString(const char* string) -{ - Write((uint8*)string, strlen(string) + 1); -} - -const uint8* NetworkPacket::Read(unsigned int size) -{ - if (read + size > NetworkPacket::size) { - return 0; - } else { - uint8* data = &GetData()[read]; - read += size; - return data; - } -} - -const char* NetworkPacket::ReadString() -{ - char* str = (char*)&GetData()[read]; - char* strend = str; - while (read < size && *strend != 0) { - read++; - strend++; - } - if (*strend != 0) { - return nullptr; - } - read++; - return str; -} - -void NetworkPacket::Clear() -{ - transferred = 0; - read = 0; - data->clear(); -} - -bool NetworkPacket::CommandRequiresAuth() -{ - switch (GetCommand()) { - case NETWORK_COMMAND_PING: - case NETWORK_COMMAND_AUTH: - case NETWORK_COMMAND_TOKEN: - case NETWORK_COMMAND_GAMEINFO: - return false; - default: - return true; - } -} - void NetworkPlayer::Read(NetworkPacket& packet) { const char* name = packet.ReadString(); @@ -371,172 +257,6 @@ void NetworkGroup::SetName(std::string name) NetworkGroup::name = name; } -NetworkConnection::NetworkConnection() -{ - authstatus = NETWORK_AUTH_NONE; - player = 0; - socket = INVALID_SOCKET; - ResetLastPacketTime(); - last_disconnect_reason = NULL; -} - -NetworkConnection::~NetworkConnection() -{ - if (socket != INVALID_SOCKET) { - closesocket(socket); - } - - if (last_disconnect_reason) { - delete[] last_disconnect_reason; - } -} - -int NetworkConnection::ReadPacket() -{ - if (inboundpacket.transferred < sizeof(inboundpacket.size)) { - // read packet size - int readBytes = recv(socket, &((char*)&inboundpacket.size)[inboundpacket.transferred], sizeof(inboundpacket.size) - inboundpacket.transferred, 0); - if (readBytes == SOCKET_ERROR || readBytes == 0) { - if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) { - return NETWORK_READPACKET_DISCONNECTED; - } else { - return NETWORK_READPACKET_NO_DATA; - } - } - inboundpacket.transferred += readBytes; - if (inboundpacket.transferred == sizeof(inboundpacket.size)) { - inboundpacket.size = ntohs(inboundpacket.size); - if(inboundpacket.size == 0){ // Can't have a size 0 packet - return NETWORK_READPACKET_DISCONNECTED; - } - inboundpacket.data->resize(inboundpacket.size); - } - } else { - // read packet data - if (inboundpacket.data->capacity() > 0) { - int readBytes = recv(socket, (char*)&inboundpacket.GetData()[inboundpacket.transferred - sizeof(inboundpacket.size)], - sizeof(inboundpacket.size) + inboundpacket.size - inboundpacket.transferred, 0); - if (readBytes == SOCKET_ERROR || readBytes == 0) { - if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) { - return NETWORK_READPACKET_DISCONNECTED; - } else { - return NETWORK_READPACKET_NO_DATA; - } - } - inboundpacket.transferred += readBytes; - } - if (inboundpacket.transferred == sizeof(inboundpacket.size) + inboundpacket.size) { - last_packet_time = SDL_GetTicks(); - return NETWORK_READPACKET_SUCCESS; - } - } - return NETWORK_READPACKET_MORE_DATA; -} - -bool NetworkConnection::SendPacket(NetworkPacket& packet) -{ - uint16 sizen = htons(packet.size); - std::vector tosend; - tosend.reserve(sizeof(sizen) + packet.size); - tosend.insert(tosend.end(), (uint8*)&sizen, (uint8*)&sizen + sizeof(sizen)); - tosend.insert(tosend.end(), packet.data->begin(), packet.data->end()); - while (1) { - int sentBytes = send(socket, (const char*)&tosend[packet.transferred], tosend.size() - packet.transferred, FLAG_NO_PIPE); - if (sentBytes == SOCKET_ERROR) { - return false; - } - packet.transferred += sentBytes; - if (packet.transferred == tosend.size()) { - return true; - } - } - return false; -} - -void NetworkConnection::QueuePacket(std::unique_ptr packet, bool front) -{ - if (authstatus == NETWORK_AUTH_OK || !packet->CommandRequiresAuth()) { - packet->size = (uint16)packet->data->size(); - if (front) { - outboundpackets.push_front(std::move(packet)); - } else { - outboundpackets.push_back(std::move(packet)); - } - } -} - -void NetworkConnection::SendQueuedPackets() -{ - while (outboundpackets.size() > 0 && SendPacket(*(outboundpackets.front()).get())) { - outboundpackets.remove(outboundpackets.front()); - } -} - -bool NetworkConnection::SetTCPNoDelay(bool on) -{ - return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on)) == 0; -} - -bool NetworkConnection::SetNonBlocking(bool on) -{ - return SetNonBlocking(socket, on); -} - -bool NetworkConnection::SetNonBlocking(SOCKET socket, bool on) -{ -#ifdef __WINDOWS__ - u_long nonblocking = on; - return ioctlsocket(socket, FIONBIO, &nonblocking) == 0; -#else - int flags = fcntl(socket, F_GETFL, 0); - return fcntl(socket, F_SETFL, on ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK)) == 0; -#endif -} - -void NetworkConnection::ResetLastPacketTime() -{ - last_packet_time = SDL_GetTicks(); -} - -bool NetworkConnection::ReceivedPacketRecently() -{ -#ifndef DEBUG - if (SDL_TICKS_PASSED(SDL_GetTicks(), last_packet_time + 7000)) { - return false; - } -#endif - return true; -} - -const char* NetworkConnection::getLastDisconnectReason() const -{ - return this->last_disconnect_reason; -} - -void NetworkConnection::setLastDisconnectReason(const char *src) -{ - if (src == nullptr) { - if (last_disconnect_reason) { - delete[] last_disconnect_reason; - last_disconnect_reason = NULL; - } - return; - } - - if (!last_disconnect_reason) { - last_disconnect_reason = new char[NETWORK_DISCONNECT_REASON_BUFFER_SIZE]; - } - - strncpy(last_disconnect_reason, src, NETWORK_DISCONNECT_REASON_BUFFER_SIZE - 1); -} - -void NetworkConnection::setLastDisconnectReason(const rct_string_id string_id, void *args) -{ - char buffer[NETWORK_DISCONNECT_REASON_BUFFER_SIZE]; - format_string(buffer, string_id, args); - setLastDisconnectReason(buffer); -} - Network::Network() { wsa_initialized = false; diff --git a/src/network/network.h b/src/network/network.h index 55d1c8486a..b16dd09a59 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -27,20 +27,6 @@ enum { NETWORK_PLAYER_FLAG_ISSERVER = 1 << 0, }; -enum { - NETWORK_AUTH_NONE, - NETWORK_AUTH_REQUESTED, - NETWORK_AUTH_OK, - NETWORK_AUTH_BADVERSION, - NETWORK_AUTH_BADNAME, - NETWORK_AUTH_BADPASSWORD, - NETWORK_AUTH_VERIFICATIONFAILURE, - NETWORK_AUTH_FULL, - NETWORK_AUTH_REQUIREPASSWORD, - NETWORK_AUTH_VERIFIED, - NETWORK_AUTH_UNKNOWN_KEY_DISALLOWED, -}; - enum { NETWORK_STATUS_NONE, NETWORK_STATUS_READY, @@ -70,40 +56,7 @@ extern "C" { #define NETWORK_STREAM_VERSION "9" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION -#define NETWORK_DISCONNECT_REASON_BUFFER_SIZE 256 - -#ifdef __WINDOWS__ - #include - #include - #define LAST_SOCKET_ERROR() WSAGetLastError() - #undef EWOULDBLOCK - #define EWOULDBLOCK WSAEWOULDBLOCK - #ifndef SHUT_RD - #define SHUT_RD SD_RECEIVE - #endif - #ifndef SHUT_RDWR - #define SHUT_RDWR SD_BOTH - #endif - #define FLAG_NO_PIPE 0 -#else - #include - #include - #include - #include - #include - #include - typedef int SOCKET; - #define SOCKET_ERROR -1 - #define INVALID_SOCKET -1 - #define LAST_SOCKET_ERROR() errno - #define closesocket close - #define ioctlsocket ioctl - #if defined(__LINUX__) - #define FLAG_NO_PIPE MSG_NOSIGNAL - #else - #define FLAG_NO_PIPE 0 - #endif // defined(__LINUX__) -#endif // __WINDOWS__ +#include "NetworkTypes.h" // Fixes issues on OS X #if defined(_RCT2_H_) && !defined(_MSC_VER) @@ -124,51 +77,11 @@ extern "C" { #include "../core/Json.hpp" #include "../core/Nullable.hpp" #include "NetworkAddress.h" +#include "NetworkConnection.h" #include "NetworkKey.h" +#include "NetworkPacket.h" #include "NetworkUser.h" -template -struct ByteSwapT { }; -template <> -struct ByteSwapT<1> { static uint8 SwapBE(uint8 value) { return value; } }; -template <> -struct ByteSwapT<2> { static uint16 SwapBE(uint16 value) { return SDL_SwapBE16(value); } }; -template <> -struct ByteSwapT<4> { static uint32 SwapBE(uint32 value) { return SDL_SwapBE32(value); } }; -template -T ByteSwapBE(const T& value) { return ByteSwapT::SwapBE(value); } - -class NetworkPacket -{ -public: - NetworkPacket(); - static std::unique_ptr Allocate(); - static std::unique_ptr Duplicate(NetworkPacket& packet); - uint8* GetData(); - uint32 GetCommand(); - template - NetworkPacket& operator<<(T value) { - T swapped = ByteSwapBE(value); uint8* bytes = (uint8*)&swapped; data->insert(data->end(), bytes, bytes + sizeof(value)); - return *this; - } - void Write(const uint8* bytes, unsigned int size); - void WriteString(const char* string); - template - NetworkPacket& operator>>(T& value) { - if (read + sizeof(value) > size) { value = 0; } else { value = ByteSwapBE(*((T*)&GetData()[read])); read += sizeof(value); } - return *this; - } - const uint8* Read(unsigned int size); - const char* ReadString(); - void Clear(); - bool CommandRequiresAuth(); - - uint16 size; - std::shared_ptr> data; - unsigned int transferred; - int read; -}; - class NetworkPlayer { public: @@ -283,39 +196,6 @@ private: std::string name; }; -class NetworkConnection -{ -public: - NetworkConnection(); - ~NetworkConnection(); - int ReadPacket(); - void QueuePacket(std::unique_ptr packet, bool front = false); - void SendQueuedPackets(); - bool SetTCPNoDelay(bool on); - bool SetNonBlocking(bool on); - static bool SetNonBlocking(SOCKET socket, bool on); - void ResetLastPacketTime(); - bool ReceivedPacketRecently(); - - const char *getLastDisconnectReason() const; - void setLastDisconnectReason(const char *src); - void setLastDisconnectReason(const rct_string_id string_id, void *args = nullptr); - - SOCKET socket = INVALID_SOCKET; - NetworkPacket inboundpacket; - int authstatus = NETWORK_AUTH_NONE; - NetworkPlayer* player; - uint32 ping_time = 0; - NetworkKey key; - std::vector challenge; - -private: - char* last_disconnect_reason; - bool SendPacket(NetworkPacket& packet); - std::list> outboundpackets; - uint32 last_packet_time; -}; - class Network { public: