From 8dfbabbd071d8f70b3a266596e74bdf4508971d1 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 1 Jun 2016 22:58:21 +0100 Subject: [PATCH 1/4] refactor network, create ITcpSocket Abstracts all socket code into a new class TcpSocket which is only exposed by a light interface, ITcpSocket. This now means that platform specific headers like winsock2.h and sys/socket.h do not have to be included in OpenRCT2 header files reducing include load and other issues. --- openrct2.vcxproj | 4 +- src/core/Exception.hpp | 13 +- src/network/NetworkAddress.cpp | 127 -------- src/network/NetworkAddress.h | 71 ----- src/network/NetworkConnection.cpp | 90 ++---- src/network/NetworkConnection.h | 11 +- src/network/NetworkTypes.h | 41 --- src/network/TcpSocket.cpp | 462 ++++++++++++++++++++++++++++++ src/network/TcpSocket.h | 63 ++++ src/network/network.cpp | 263 +++++++---------- src/network/network.h | 15 +- 11 files changed, 674 insertions(+), 486 deletions(-) delete mode 100644 src/network/NetworkAddress.cpp delete mode 100644 src/network/NetworkAddress.h create mode 100644 src/network/TcpSocket.cpp create mode 100644 src/network/TcpSocket.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 4ee5ba88a4..494eed8401 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -84,13 +84,13 @@ - + @@ -365,13 +365,13 @@ - + diff --git a/src/core/Exception.hpp b/src/core/Exception.hpp index 3685f159e5..c453547fd2 100644 --- a/src/core/Exception.hpp +++ b/src/core/Exception.hpp @@ -19,23 +19,26 @@ #include "../common.h" #include +#include class Exception : public std::exception { public: Exception() : Exception(nullptr) { } - Exception(const char * message) : std::exception() + Exception(const char * message) : Exception(std::string(message)) { } + + Exception(const std::string &message) : std::exception() { _message = message; } virtual ~Exception() { } - const char * what() const throw() override { return _message; } - const char * GetMessage() const { return _message; } - const char * GetMsg() const { return _message; } + const char * what() const throw() override { return _message.c_str(); } + const char * GetMessage() const { return _message.c_str(); } + const char * GetMsg() const { return _message.c_str(); } private: - const char * _message; + std::string _message; }; diff --git a/src/network/NetworkAddress.cpp b/src/network/NetworkAddress.cpp deleted file mode 100644 index c050ebc266..0000000000 --- a/src/network/NetworkAddress.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#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 - -#ifndef DISABLE_NETWORK - -#include -#include -#include "NetworkAddress.h" - -NetworkAddress::NetworkAddress() -{ - _result = std::make_shared(); - _result->status = RESOLVE_NONE; - _resolveMutex = SDL_CreateMutex(); -} - -NetworkAddress::~NetworkAddress() -{ - SDL_DestroyMutex(_resolveMutex); -} - -void NetworkAddress::Resolve(const char * host, uint16 port) -{ - SDL_LockMutex(_resolveMutex); - { - // Create a new result store - _result = std::make_shared(); - _result->status = RESOLVE_INPROGRESS; - - // Create a new request - auto req = new ResolveRequest(); - req->Host = std::string(host == nullptr ? "" : host); - req->Port = port;; - req->Result = _result; - - // Resolve synchronously - ResolveWorker(req); - } - SDL_UnlockMutex(_resolveMutex); -} - -void NetworkAddress::ResolveAsync(const char * host, uint16 port) -{ - SDL_LockMutex(_resolveMutex); - { - // Create a new result store - _result = std::make_shared(); - _result->status = RESOLVE_INPROGRESS; - - // Create a new request - auto req = new ResolveRequest(); - req->Host = std::string(host); - req->Port = port; - req->Result = _result; - - // Spin off a worker thread for resolving the address - SDL_CreateThread([](void * pointer) -> int - { - ResolveWorker((ResolveRequest *)pointer); - return 0; - }, 0, req); - } - SDL_UnlockMutex(_resolveMutex); -} - -NetworkAddress::RESOLVE_STATUS NetworkAddress::GetResult(sockaddr_storage * ss, int * ss_len) -{ - SDL_LockMutex(_resolveMutex); - { - const ResolveResult * result = _result.get(); - if (result->status == RESOLVE_OK) - { - *ss = result->ss; - *ss_len = result->ss_len; - } - return result->status; - } - SDL_UnlockMutex(_resolveMutex); -} - -void NetworkAddress::ResolveWorker(ResolveRequest * req) -{ - // Resolve the address - const char * nodeName = req->Host.c_str(); - std::string serviceName = std::to_string(req->Port); - - addrinfo hints = { 0 }; - hints.ai_family = AF_UNSPEC; - if (req->Host.empty()) - { - hints.ai_flags = AI_PASSIVE; - nodeName = nullptr; - } - - addrinfo * result; - getaddrinfo(nodeName, serviceName.c_str(), &hints, &result); - - // Store the result - ResolveResult * resolveResult = req->Result.get(); - if (result != nullptr) - { - resolveResult->status = RESOLVE_OK; - memcpy(&resolveResult->ss, result->ai_addr, result->ai_addrlen); - resolveResult->ss_len = result->ai_addrlen; - freeaddrinfo(result); - } - else - { - resolveResult->status = RESOLVE_FAILED; - } - delete req; -} - -#endif diff --git a/src/network/NetworkAddress.h b/src/network/NetworkAddress.h deleted file mode 100644 index 2b479891ea..0000000000 --- a/src/network/NetworkAddress.h +++ /dev/null @@ -1,71 +0,0 @@ -#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 "NetworkTypes.h" -#include "../common.h" - -class NetworkAddress final -{ -public: - enum RESOLVE_STATUS - { - RESOLVE_NONE, - RESOLVE_INPROGRESS, - RESOLVE_OK, - RESOLVE_FAILED - }; - - NetworkAddress(); - ~NetworkAddress(); - - void Resolve(const char * host, uint16 port); - void ResolveAsync(const char * host, uint16 port); - - RESOLVE_STATUS GetResult(sockaddr_storage * ss, int * ss_len); - -private: - struct ResolveResult - { - RESOLVE_STATUS status; - sockaddr_storage ss; - int ss_len; - }; - - struct ResolveRequest - { - std::string Host; - uint16 Port; - std::shared_ptr Result; - }; - - /** - * Store for the async result. A new store is created for every request. - * Old requests simply write to an old store that will then be - * automatically deleted by std::shared_ptr. - */ - std::shared_ptr _result; - - /** - * Mutex so synchronoise the requests. - */ - SDL_mutex * _resolveMutex; - - static void ResolveWorker(ResolveRequest * req); -}; diff --git a/src/network/NetworkConnection.cpp b/src/network/NetworkConnection.cpp index 27aaaee400..d73e8e7cf6 100644 --- a/src/network/NetworkConnection.cpp +++ b/src/network/NetworkConnection.cpp @@ -16,6 +16,7 @@ #ifndef DISABLE_NETWORK +#include "Network.h" #include "NetworkConnection.h" #include "../core/String.hpp" #include @@ -34,10 +35,7 @@ NetworkConnection::NetworkConnection() NetworkConnection::~NetworkConnection() { - if (Socket != INVALID_SOCKET) - { - closesocket(Socket); - } + delete Socket; if (_lastDisconnectReason) { delete[] _lastDisconnectReason; @@ -49,22 +47,19 @@ 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) + void * buffer = &((char*)&InboundPacket.size)[InboundPacket.transferred]; + size_t bufferLength = sizeof(InboundPacket.size) - InboundPacket.transferred; + size_t readBytes; + NETWORK_READPACKET status = Socket->ReceiveData(buffer, bufferLength, &readBytes); + if (status != NETWORK_READPACKET_SUCCESS) { - if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) - { - return NETWORK_READPACKET_DISCONNECTED; - } - else - { - return NETWORK_READPACKET_NO_DATA; - } + return status; } + InboundPacket.transferred += readBytes; if (InboundPacket.transferred == sizeof(InboundPacket.size)) { - InboundPacket.size = ntohs(InboundPacket.size); + InboundPacket.size = Convert::NetworkToHost(InboundPacket.size); if (InboundPacket.size == 0) // Can't have a size 0 packet { return NETWORK_READPACKET_DISCONNECTED; @@ -77,21 +72,15 @@ int NetworkConnection::ReadPacket() // 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) + void * buffer = &InboundPacket.GetData()[InboundPacket.transferred - sizeof(InboundPacket.size)]; + size_t bufferLength = sizeof(InboundPacket.size) + InboundPacket.size - InboundPacket.transferred; + size_t readBytes; + NETWORK_READPACKET status = Socket->ReceiveData(buffer, bufferLength, &readBytes); + if (status != NETWORK_READPACKET_SUCCESS) { - if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) - { - return NETWORK_READPACKET_DISCONNECTED; - } - else - { - return NETWORK_READPACKET_NO_DATA; - } + return status; } + InboundPacket.transferred += readBytes; } if (InboundPacket.transferred == sizeof(InboundPacket.size) + InboundPacket.size) @@ -105,25 +94,23 @@ int NetworkConnection::ReadPacket() bool NetworkConnection::SendPacket(NetworkPacket& packet) { - uint16 sizen = htons(packet.size); + uint16 sizen = Convert::HostToNetwork(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) + + const void * buffer = &tosend[packet.transferred]; + size_t bufferSize = tosend.size() - packet.transferred; + if (Socket->SendData(buffer, bufferSize)) { - 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; - } + packet.transferred += bufferSize; + return true; + } + else + { + return false; } - return false; } void NetworkConnection::QueuePacket(std::unique_ptr packet, bool front) @@ -150,27 +137,6 @@ void NetworkConnection::SendQueuedPackets() } } -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() { _lastPacketTime = SDL_GetTicks(); diff --git a/src/network/NetworkConnection.h b/src/network/NetworkConnection.h index 44e3fced5c..1e5bb56a11 100644 --- a/src/network/NetworkConnection.h +++ b/src/network/NetworkConnection.h @@ -19,17 +19,20 @@ #include #include #include + +#include "../common.h" + #include "NetworkTypes.h" #include "NetworkKey.h" #include "NetworkPacket.h" -#include "../common.h" +#include "TcpSocket.h" class NetworkPlayer; class NetworkConnection { public: - SOCKET Socket = INVALID_SOCKET; + ITcpSocket * Socket = nullptr; NetworkPacket InboundPacket; NETWORK_AUTH AuthStatus = NETWORK_AUTH_NONE; NetworkPlayer * Player = nullptr; @@ -43,8 +46,6 @@ public: int ReadPacket(); void QueuePacket(std::unique_ptr packet, bool front = false); void SendQueuedPackets(); - bool SetTCPNoDelay(bool on); - bool SetNonBlocking(bool on); void ResetLastPacketTime(); bool ReceivedPacketRecently(); @@ -52,8 +53,6 @@ public: void SetLastDisconnectReason(const utf8 * src); void SetLastDisconnectReason(const rct_string_id string_id, void * args = nullptr); - static bool SetNonBlocking(SOCKET socket, bool on); - private: std::list> _outboundPackets; uint32 _lastPacketTime; diff --git a/src/network/NetworkTypes.h b/src/network/NetworkTypes.h index 1f1db18354..0c933fba2b 100644 --- a/src/network/NetworkTypes.h +++ b/src/network/NetworkTypes.h @@ -20,52 +20,11 @@ #include #ifndef DISABLE_NETWORK -#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" #endif -enum NETWORK_READPACKET -{ - NETWORK_READPACKET_SUCCESS, - NETWORK_READPACKET_NO_DATA, - NETWORK_READPACKET_MORE_DATA, - NETWORK_READPACKET_DISCONNECTED -}; - enum NETWORK_AUTH { NETWORK_AUTH_NONE, diff --git a/src/network/TcpSocket.cpp b/src/network/TcpSocket.cpp new file mode 100644 index 0000000000..c4df396837 --- /dev/null +++ b/src/network/TcpSocket.cpp @@ -0,0 +1,462 @@ +#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 + +#ifndef DISABLE_NETWORK + +// MSVC: include here otherwise PI gets defined twice +#include + +#include +#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 "../core/Exception.hpp" +#include "TcpSocket.h" + +constexpr uint32 CONNECT_TIMEOUT_MS = 3000; + +class TcpSocket; + +class SocketException : public Exception +{ +public: + SocketException(const char * message) : Exception(message) { } + SocketException(const std::string &message) : Exception(message) { } +}; + +struct ConnectRequest +{ + TcpSocket * TcpSocket; + std::string Address; + uint16 Port; +}; + +class TcpSocket : public ITcpSocket +{ +private: + SOCKET_STATUS _status = SOCKET_STATUS_CLOSED; + uint16 _listeningPort = 0; + SOCKET _socket = INVALID_SOCKET; + + SDL_mutex * _connectMutex = nullptr; + std::string _error; + +public: + TcpSocket() + { + _connectMutex = SDL_CreateMutex(); + } + + ~TcpSocket() override + { + SDL_LockMutex(_connectMutex); + { + CloseSocket(); + } + SDL_UnlockMutex(_connectMutex); + SDL_DestroyMutex(_connectMutex); + } + + SOCKET_STATUS GetStatus() override + { + return _status; + } + + const char * GetError() override + { + return _error.empty() ? nullptr : _error.c_str(); + } + + void Listen(uint16 port) override + { + Listen(nullptr, port); + } + + void Listen(const char * address, uint16 port) override + { + if (_status != SOCKET_STATUS_CLOSED) + { + throw Exception("Socket not closed."); + } + + sockaddr_storage ss; + int ss_len; + if (!ResolveAddress(address, port, &ss, &ss_len)) + { + throw SocketException("Unable to resolve address."); + } + + // Create the listening socket + _socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (_socket == INVALID_SOCKET) + { + throw SocketException("Unable to create socket."); + } + + // Turn off IPV6_V6ONLY so we can accept both v4 and v6 connections + int value = 0; + if (setsockopt(_socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&value, sizeof(value)) != 0) + { + log_error("IPV6_V6ONLY failed. %d", LAST_SOCKET_ERROR()); + } + + try + { + // Bind to address:port and listen + if (bind(_socket, (sockaddr *)&ss, ss_len) != 0) + { + throw SocketException("Unable to bind to socket."); + } + if (listen(_socket, SOMAXCONN) != 0) + { + throw SocketException("Unable to listen on socket."); + } + + if (!SetNonBlocking(_socket, true)) + { + throw SocketException("Failed to set non-blocking mode."); + } + } + catch (Exception ex) + { + CloseSocket(); + throw; + } + + _listeningPort = port; + _status = SOCKET_STATUS_LISTENING; + } + + ITcpSocket * Accept() override + { + if (_status != SOCKET_STATUS_LISTENING) + { + throw Exception("Socket not listening."); + } + + ITcpSocket * tcpSocket = nullptr; + SOCKET socket = accept(_socket, nullptr, nullptr); + if (socket == INVALID_SOCKET) + { + if (LAST_SOCKET_ERROR() != EWOULDBLOCK) + { + log_error("Failed to accept client."); + } + } + else + { + if (!SetNonBlocking(socket, true)) + { + closesocket(socket); + log_error("Failed to set non-blocking mode."); + } + else + { + SetTCPNoDelay(socket, true); + tcpSocket = new TcpSocket(socket); + } + } + return tcpSocket; + } + + void Connect(const char * address, uint16 port) override + { + if (_status != SOCKET_STATUS_CLOSED) + { + throw Exception("Socket not closed."); + } + + try + { + // Resolve address + _status = SOCKET_STATUS_RESOLVING; + + sockaddr_storage ss; + int ss_len; + if (!ResolveAddress(address, port, &ss, &ss_len)) + { + throw SocketException("Unable to resolve address."); + } + + _status = SOCKET_STATUS_CONNECTING; + _socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (_socket == INVALID_SOCKET) + { + throw SocketException("Unable to create socket."); + } + + SetTCPNoDelay(_socket, true); + if (!SetNonBlocking(_socket, true)) + { + throw SocketException("Failed to set non-blocking mode."); + } + + // Connect + uint32 connectStartTick; + int connectResult = connect(_socket, (sockaddr *)&ss, ss_len); + if (connectResult != SOCKET_ERROR || (LAST_SOCKET_ERROR() != EINPROGRESS && + LAST_SOCKET_ERROR() != EWOULDBLOCK)) + { + throw SocketException("Failed to connect."); + } + + connectStartTick = SDL_GetTicks(); + + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) + { + throw SocketException("getsockopt failed with error: " + std::to_string(LAST_SOCKET_ERROR())); + } + if (error != 0) + { + throw SocketException("Connection failed: " + std::to_string(error)); + } + + do + { + // Sleep for a bit + SDL_Delay(100); + + fd_set writeFD; + FD_ZERO(&writeFD); + FD_SET(_socket, &writeFD); + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select(_socket + 1, nullptr, &writeFD, nullptr, &timeout) > 0) + { + error = 0; + socklen_t len = sizeof(error); + if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) + { + throw SocketException("getsockopt failed with error: " + std::to_string(LAST_SOCKET_ERROR())); + } + if (error == 0) + { + _status = SOCKET_STATUS_CONNECTED; + return; + } + } + } while (!SDL_TICKS_PASSED(SDL_GetTicks(), connectStartTick + CONNECT_TIMEOUT_MS)); + + // Connection request timed out + throw SocketException("Connection timed out."); + } + catch (Exception ex) + { + CloseSocket(); + throw; + } + } + + void ConnectAsync(const char * address, uint16 port) override + { + if (_status != SOCKET_STATUS_CLOSED) + { + throw Exception("Socket not closed."); + } + + if (SDL_TryLockMutex(_connectMutex) == 0) + { + // Spin off a worker thread for resolving the address + auto req = new ConnectRequest(); + req->TcpSocket = this; + req->Address = std::string(address); + req->Port = port; + SDL_CreateThread([](void * pointer) -> int + { + auto req = (ConnectRequest *)pointer; + try + { + req->TcpSocket->Connect(req->Address.c_str(), req->Port); + } + catch (Exception ex) + { + req->TcpSocket->_error = std::string(ex.GetMsg()); + } + delete req; + + SDL_UnlockMutex(req->TcpSocket->_connectMutex); + return 0; + }, 0, req); + } + } + + void Disconnect() override + { + if (_status == SOCKET_STATUS_CONNECTED) + { + shutdown(_socket, SHUT_RDWR); + } + } + + bool SendData(const void * buffer, size_t size) override + { + if (_status != SOCKET_STATUS_CONNECTED) + { + throw Exception("Socket not connected."); + } + + size_t totalSent = 0; + do + { + int sentBytes = send(_socket, (const char *)buffer, (int)size, FLAG_NO_PIPE); + if (sentBytes == SOCKET_ERROR) + { + return false; + } + totalSent += sentBytes; + } while (totalSent < size); + return true; + } + + NETWORK_READPACKET ReceiveData(void * buffer, size_t size, size_t * sizeReceived) override + { + if (_status != SOCKET_STATUS_CONNECTED) + { + throw Exception("Socket not connected."); + } + + int readBytes = recv(_socket, (char *)buffer, size, 0); + if (readBytes == SOCKET_ERROR || readBytes <= 0) + { + *sizeReceived = 0; + if (LAST_SOCKET_ERROR() != EWOULDBLOCK && LAST_SOCKET_ERROR() != EAGAIN) + { + return NETWORK_READPACKET_DISCONNECTED; + } + else + { + return NETWORK_READPACKET_NO_DATA; + } + } + else + { + *sizeReceived = readBytes; + return NETWORK_READPACKET_SUCCESS; + } + } + + void Close() + { + SDL_LockMutex(_connectMutex); + { + CloseSocket(); + } + SDL_UnlockMutex(_connectMutex); + } + +private: + TcpSocket(SOCKET socket) + { + _socket = socket; + _status = SOCKET_STATUS_CONNECTED; + } + + void CloseSocket() + { + if (_socket != INVALID_SOCKET) + { + closesocket(_socket); + _socket = INVALID_SOCKET; + } + _status = SOCKET_STATUS_CLOSED; + } + + bool ResolveAddress(const char * address, uint16 port, sockaddr_storage * ss, int * ss_len) + { + std::string serviceName = std::to_string(port); + + addrinfo hints = { 0 }; + hints.ai_family = AF_UNSPEC; + if (address == nullptr) + { + hints.ai_flags = AI_PASSIVE; + } + + addrinfo * result; + getaddrinfo(address, serviceName.c_str(), &hints, &result); + if (result == nullptr) + { + return false; + } + else + { + memcpy(ss, result->ai_addr, result->ai_addrlen); + *ss_len = result->ai_addrlen; + return true; + } + } + + static bool 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 + } + + static bool SetTCPNoDelay(SOCKET socket, bool enabled) + { + return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&enabled, sizeof(enabled)) == 0; + } +}; + +ITcpSocket * CreateTcpSocket() +{ + return new TcpSocket(); +} + +#endif diff --git a/src/network/TcpSocket.h b/src/network/TcpSocket.h new file mode 100644 index 0000000000..61cbe32a70 --- /dev/null +++ b/src/network/TcpSocket.h @@ -0,0 +1,63 @@ +#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 "../common.h" + +enum SOCKET_STATUS +{ + SOCKET_STATUS_CLOSED, + SOCKET_STATUS_RESOLVING, + SOCKET_STATUS_CONNECTING, + SOCKET_STATUS_CONNECTED, + SOCKET_STATUS_LISTENING, +}; + +enum NETWORK_READPACKET +{ + NETWORK_READPACKET_SUCCESS, + NETWORK_READPACKET_NO_DATA, + NETWORK_READPACKET_MORE_DATA, + NETWORK_READPACKET_DISCONNECTED +}; + +/** + * Represents a TCP socket / connection or listener. + */ +interface ITcpSocket +{ +public: + virtual ~ITcpSocket() { } + + virtual SOCKET_STATUS GetStatus() abstract; + virtual const char * GetError() abstract; + + virtual void Listen(uint16 port) abstract; + virtual void Listen(const char * address, uint16 port) abstract; + virtual ITcpSocket * Accept() abstract; + + virtual void Connect(const char * address, uint16 port) abstract; + virtual void ConnectAsync(const char * address, uint16 port) abstract; + + virtual bool SendData(const void * buffer, size_t size) abstract; + virtual NETWORK_READPACKET ReceiveData(void * buffer, size_t size, size_t * sizeReceived) abstract; + + virtual void Disconnect() abstract; + virtual void Close() abstract; +}; + +ITcpSocket * CreateTcpSocket(); diff --git a/src/network/network.cpp b/src/network/network.cpp index e4de298a61..335d795f0d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -14,6 +14,15 @@ *****************************************************************************/ #pragma endregion +#include + +#ifdef __WINDOWS__ + // winsock2 must be included before windows.h + #include +#else + #include +#endif + extern "C" { #include "../openrct2.h" #include "../platform/platform.h" @@ -161,14 +170,16 @@ void Network::Close() return; } if (mode == NETWORK_MODE_CLIENT) { - closesocket(server_connection.Socket); - } else - if (mode == NETWORK_MODE_SERVER) { - closesocket(listening_socket); + delete server_connection.Socket; + server_connection.Socket = nullptr; + } else if (mode == NETWORK_MODE_SERVER) { + delete listening_socket; + listening_socket = nullptr; } mode = NETWORK_MODE_NONE; status = NETWORK_STATUS_NONE; + _lastConnectStatus = SOCKET_STATUS_CLOSED; server_connection.AuthStatus = NETWORK_AUTH_NONE; server_connection.InboundPacket.Clear(); server_connection.SetLastDisconnectReason(nullptr); @@ -199,14 +210,11 @@ bool Network::BeginClient(const char* host, unsigned short port) if (!Init()) return false; - server_address.ResolveAsync(host, port); - status = NETWORK_STATUS_RESOLVING; - - char str_resolving[256]; - format_string(str_resolving, STR_MULTIPLAYER_RESOLVING, NULL); - window_network_status_open(str_resolving, []() -> void { - gNetwork.Close(); - }); + assert(server_connection.Socket == nullptr); + server_connection.Socket = CreateTcpSocket(); + server_connection.Socket->ConnectAsync(host, port); + status = NETWORK_STATUS_CONNECTING; + _lastConnectStatus = SOCKET_STATUS_CLOSED; BeginChatLog(); @@ -272,43 +280,11 @@ bool Network::BeginServer(unsigned short port, const char* address) _userManager.Load(); - NetworkAddress networkaddress; - networkaddress.Resolve(address, port); - - sockaddr_storage ss; - int ss_len; - networkaddress.GetResult(&ss, &ss_len); - log_verbose("Begin listening for clients"); - listening_socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); - if (listening_socket == INVALID_SOCKET) { - log_error("Unable to create socket."); - return false; - } - // Turn off IPV6_V6ONLY so we can accept both v4 and v6 connections - int value = 0; - if (setsockopt(listening_socket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&value, sizeof(value)) != 0) { - log_error("IPV6_V6ONLY failed. %d", LAST_SOCKET_ERROR()); - } - - if (bind(listening_socket, (sockaddr *)&ss, ss_len) != 0) { - closesocket(listening_socket); - log_error("Unable to bind to socket."); - return false; - } - - if (listen(listening_socket, SOMAXCONN) != 0) { - closesocket(listening_socket); - log_error("Unable to listen on socket."); - return false; - } - - if (!NetworkConnection::SetNonBlocking(listening_socket, true)) { - closesocket(listening_socket); - log_error("Failed to set non-blocking mode."); - return false; - } + assert(listening_socket == nullptr); + listening_socket = CreateTcpSocket(); + listening_socket->Listen(address, port); ServerName = gConfigNetwork.server_name; ServerDescription = gConfigNetwork.server_description; @@ -421,113 +397,74 @@ void Network::UpdateServer() break; } - SOCKET socket = accept(listening_socket, NULL, NULL); - if (socket == INVALID_SOCKET) { - if (LAST_SOCKET_ERROR() != EWOULDBLOCK) { - PrintError(); - log_error("Failed to accept client."); - } - } else { - if (!NetworkConnection::SetNonBlocking(socket, true)) { - closesocket(socket); - log_error("Failed to set non-blocking mode."); - } else { - AddClient(socket); - } + ITcpSocket * tcpSocket = listening_socket->Accept(); + if (tcpSocket != nullptr) { + AddClient(tcpSocket); } } void Network::UpdateClient() { - bool connectfailed = false; switch(status){ - case NETWORK_STATUS_RESOLVING:{ - sockaddr_storage ss; - int ss_len; - NetworkAddress::RESOLVE_STATUS result = server_address.GetResult(&ss, &ss_len); - - if (result == NetworkAddress::RESOLVE_OK) { - server_connection.Socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); - if (server_connection.Socket == INVALID_SOCKET) { - log_error("Unable to create socket."); - connectfailed = true; - break; + case NETWORK_STATUS_CONNECTING: + { + switch (server_connection.Socket->GetStatus()) { + case SOCKET_STATUS_RESOLVING: + { + if (_lastConnectStatus != SOCKET_STATUS_RESOLVING) + { + _lastConnectStatus = SOCKET_STATUS_RESOLVING; + char str_resolving[256]; + format_string(str_resolving, STR_MULTIPLAYER_RESOLVING, NULL); + window_network_status_open(str_resolving, []() -> void { + gNetwork.Close(); + }); } - - server_connection.SetTCPNoDelay(true); - if (!server_connection.SetNonBlocking(true)) { - log_error("Failed to set non-blocking mode."); - connectfailed = true; - break; - } - - if (connect(server_connection.Socket, (sockaddr *)&ss, ss_len) == SOCKET_ERROR && - (LAST_SOCKET_ERROR() == EINPROGRESS || LAST_SOCKET_ERROR() == EWOULDBLOCK) - ) { + break; + } + case SOCKET_STATUS_CONNECTING: + { + if (_lastConnectStatus != SOCKET_STATUS_CONNECTING) + { + _lastConnectStatus = SOCKET_STATUS_CONNECTING; char str_connecting[256]; format_string(str_connecting, STR_MULTIPLAYER_CONNECTING, NULL); window_network_status_open(str_connecting, []() -> void { gNetwork.Close(); }); server_connect_time = SDL_GetTicks(); - status = NETWORK_STATUS_CONNECTING; - } else { - log_error("connect() failed %d", LAST_SOCKET_ERROR()); - connectfailed = true; - break; } - } else if (result == NetworkAddress::RESOLVE_INPROGRESS) { - break; - } else { - log_error("Could not resolve address."); - connectfailed = true; - } - }break; - case NETWORK_STATUS_CONNECTING:{ - int error = 0; - socklen_t len = sizeof(error); - int result = getsockopt(server_connection.Socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len); - if (result != 0) { - log_error("getsockopt failed with error %d", LAST_SOCKET_ERROR()); break; } - if (error != 0) { - log_error("Connection failed %d", error); - connectfailed = true; + case NETWORK_STATUS_CONNECTED: + { + status = NETWORK_STATUS_CONNECTED; + server_connection.ResetLastPacketTime(); + Client_Send_TOKEN(); + char str_authenticating[256]; + format_string(str_authenticating, STR_MULTIPLAYER_AUTHENTICATING, NULL); + window_network_status_open(str_authenticating, []() -> void { + gNetwork.Close(); + }); break; } - if (SDL_TICKS_PASSED(SDL_GetTicks(), server_connect_time + 3000)) { - log_error("Connection timed out."); - connectfailed = true; - break; - } - fd_set writeFD; - FD_ZERO(&writeFD); - FD_SET(server_connection.Socket, &writeFD); - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (select(server_connection.Socket + 1, NULL, &writeFD, NULL, &timeout) > 0) { - error = 0; - socklen_t len = sizeof(error); - result = getsockopt(server_connection.Socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len); - if (result != 0) { - log_error("getsockopt failed with error %d", LAST_SOCKET_ERROR()); - break; - } - if (error == 0) { - status = NETWORK_STATUS_CONNECTED; - server_connection.ResetLastPacketTime(); - Client_Send_TOKEN(); - char str_authenticating[256]; - format_string(str_authenticating, STR_MULTIPLAYER_AUTHENTICATING, NULL); - window_network_status_open(str_authenticating, []() -> void { - gNetwork.Close(); - }); + default: + { + const char * error = server_connection.Socket->GetError(); + if (error != nullptr) { + Console::Error::WriteLine(error); } + + Close(); + window_network_status_close(); + window_error_open(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_NONE); + break; } - }break; + } + break; + } case NETWORK_STATUS_CONNECTED: + { if (!ProcessConnection(server_connection)) { // Do not show disconnect message window when password window closed/canceled if (server_connection.AuthStatus == NETWORK_AUTH_REQUIREPASSWORD) { @@ -560,11 +497,6 @@ void Network::UpdateClient() } break; } - - if (connectfailed) { - Close(); - window_network_status_close(); - window_error_open(STR_UNABLE_TO_CONNECT_TO_SERVER, STR_NONE); } } @@ -659,7 +591,7 @@ void Network::KickPlayer(int playerId) char str_disconnect_msg[256]; format_string(str_disconnect_msg, STR_MULTIPLAYER_KICKED_REASON, NULL); Server_Send_SETDISCONNECTMSG(*(*it), str_disconnect_msg); - shutdown((*it)->Socket, SHUT_RD); + (*it)->Socket->Disconnect(); (*it)->SendQueuedPackets(); break; } @@ -674,7 +606,7 @@ void Network::SetPassword(const char* password) void Network::ShutdownClient() { if (GetMode() == NETWORK_MODE_CLIENT) { - shutdown(server_connection.Socket, SHUT_RDWR); + server_connection.Socket->Disconnect(); } } @@ -1035,8 +967,8 @@ void Network::Server_Send_AUTH(NetworkConnection& connection) } connection.QueuePacket(std::move(packet)); if (connection.AuthStatus != NETWORK_AUTH_OK && connection.AuthStatus != NETWORK_AUTH_REQUIREPASSWORD) { - shutdown(connection.Socket, SHUT_RD); connection.SendQueuedPackets(); + connection.Socket->Disconnect(); } } @@ -1324,11 +1256,10 @@ void Network::ProcessGameCommandQueue() } } -void Network::AddClient(SOCKET socket) +void Network::AddClient(ITcpSocket * socket) { auto connection = std::unique_ptr(new NetworkConnection); // change to make_unique in c++14 connection->Socket = socket; - connection->SetTCPNoDelay(true); client_connection_list.push_back(std::move(connection)); } @@ -1442,21 +1373,6 @@ std::string Network::MakePlayerNameUnique(const std::string &name) return new_name; } -void Network::PrintError() -{ -#ifdef __WINDOWS__ - wchar_t *s = NULL; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, - LAST_SOCKET_ERROR(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&s, 0, NULL); - fprintf(stderr, "%S\n", s); - LocalFree(s); -#else - char *s = strerror(LAST_SOCKET_ERROR()); - fprintf(stderr, "%s\n", s); -#endif - -} - void Network::Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet) { utf8 keyPath[MAX_PATH]; @@ -1471,7 +1387,7 @@ void Network::Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& if (!ok) { log_error("Failed to load key %s", keyPath); connection.SetLastDisconnectReason(STR_MULTIPLAYER_VERIFICATION_FAILURE); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); return; } uint32 challenge_size; @@ -1486,7 +1402,7 @@ void Network::Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& if (!ok) { log_error("Failed to sign server's challenge."); connection.SetLastDisconnectReason(STR_MULTIPLAYER_VERIFICATION_FAILURE); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); return; } // Don't keep private key in memory. There's no need and it may get leaked @@ -1505,37 +1421,37 @@ void Network::Client_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p break; case NETWORK_AUTH_BADNAME: connection.SetLastDisconnectReason(STR_MULTIPLAYER_BAD_PLAYER_NAME); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; case NETWORK_AUTH_BADVERSION: { const char *version = packet.ReadString(); connection.SetLastDisconnectReason(STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION, &version); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; } case NETWORK_AUTH_BADPASSWORD: connection.SetLastDisconnectReason(STR_MULTIPLAYER_BAD_PASSWORD); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; case NETWORK_AUTH_VERIFICATIONFAILURE: connection.SetLastDisconnectReason(STR_MULTIPLAYER_VERIFICATION_FAILURE); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; case NETWORK_AUTH_FULL: connection.SetLastDisconnectReason(STR_MULTIPLAYER_SERVER_FULL); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; case NETWORK_AUTH_REQUIREPASSWORD: window_network_status_open_password(); break; case NETWORK_AUTH_UNKNOWN_KEY_DISALLOWED: connection.SetLastDisconnectReason(STR_MULTIPLAYER_UNKNOWN_KEY_DISALLOWED); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; default: connection.SetLastDisconnectReason(STR_MULTIPLAYER_INCORRECT_SOFTWARE_VERSION); - shutdown(connection.Socket, SHUT_RDWR); + connection.Socket->Disconnect(); break; } } @@ -1964,6 +1880,19 @@ void Network::Client_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacke json_decref(root); } +namespace Convert +{ + uint16 HostToNetwork(uint16 value) + { + return htons(value); + } + + uint16 NetworkToHost(uint16 value) + { + return ntohs(value); + } +} + int network_init() { return gNetwork.Init(); diff --git a/src/network/network.h b/src/network/network.h index 7a81d6f4df..a436a13129 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -30,7 +30,6 @@ enum { enum { NETWORK_STATUS_NONE, NETWORK_STATUS_READY, - NETWORK_STATUS_RESOLVING, NETWORK_STATUS_CONNECTING, NETWORK_STATUS_CONNECTED }; @@ -76,13 +75,13 @@ extern "C" { #include #include "../core/Json.hpp" #include "../core/Nullable.hpp" -#include "NetworkAddress.h" #include "NetworkConnection.h" #include "NetworkGroup.h" #include "NetworkKey.h" #include "NetworkPacket.h" #include "NetworkPlayer.h" #include "NetworkUser.h" +#include "TcpSocket.h" class Network { @@ -162,7 +161,7 @@ private: bool ProcessConnection(NetworkConnection& connection); void ProcessPacket(NetworkConnection& connection, NetworkPacket& packet); void ProcessGameCommandQueue(); - void AddClient(SOCKET socket); + void AddClient(ITcpSocket * socket); void RemoveClient(std::unique_ptr& connection); NetworkPlayer* AddPlayer(const utf8 *name, const std::string &keyhash); std::string MakePlayerNameUnique(const std::string &name); @@ -187,11 +186,11 @@ private: int mode = NETWORK_MODE_NONE; int status = NETWORK_STATUS_NONE; - NetworkAddress server_address; bool wsa_initialized = false; - SOCKET listening_socket = INVALID_SOCKET; + ITcpSocket * listening_socket = nullptr; unsigned short listening_port = 0; NetworkConnection server_connection; + SOCKET_STATUS _lastConnectStatus; uint32 last_tick_sent_time = 0; uint32 last_ping_sent_time = 0; uint32 server_tick = 0; @@ -242,6 +241,12 @@ private: void Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet); }; +namespace Convert +{ + uint16 HostToNetwork(uint16 value); + uint16 NetworkToHost(uint16 value); +} + #endif // __cplusplus #else /* DISABLE_NETWORK */ #define NETWORK_STREAM_ID "Multiplayer disabled" From e3d04ff96d44040dab91d5a60393710961a44a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Thu, 2 Jun 2016 10:13:25 +0200 Subject: [PATCH 2/4] Fix TcpSocket for Linux compilers (#13) --- src/network/NetworkConnection.cpp | 2 +- src/network/TcpSocket.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/network/NetworkConnection.cpp b/src/network/NetworkConnection.cpp index d73e8e7cf6..4d5081deba 100644 --- a/src/network/NetworkConnection.cpp +++ b/src/network/NetworkConnection.cpp @@ -16,7 +16,7 @@ #ifndef DISABLE_NETWORK -#include "Network.h" +#include "network.h" #include "NetworkConnection.h" #include "../core/String.hpp" #include diff --git a/src/network/TcpSocket.cpp b/src/network/TcpSocket.cpp index c4df396837..c9598d524c 100644 --- a/src/network/TcpSocket.cpp +++ b/src/network/TcpSocket.cpp @@ -74,7 +74,7 @@ public: struct ConnectRequest { - TcpSocket * TcpSocket; + TcpSocket * Socket; std::string Address; uint16 Port; }; @@ -309,7 +309,7 @@ public: { // Spin off a worker thread for resolving the address auto req = new ConnectRequest(); - req->TcpSocket = this; + req->Socket = this; req->Address = std::string(address); req->Port = port; SDL_CreateThread([](void * pointer) -> int @@ -317,15 +317,15 @@ public: auto req = (ConnectRequest *)pointer; try { - req->TcpSocket->Connect(req->Address.c_str(), req->Port); + req->Socket->Connect(req->Address.c_str(), req->Port); } catch (Exception ex) { - req->TcpSocket->_error = std::string(ex.GetMsg()); + req->Socket->_error = std::string(ex.GetMsg()); } delete req; - - SDL_UnlockMutex(req->TcpSocket->_connectMutex); + + SDL_UnlockMutex(req->Socket->_connectMutex); return 0; }, 0, req); } @@ -386,7 +386,7 @@ public: } } - void Close() + void Close() override { SDL_LockMutex(_connectMutex); { From 473d574a881bdf47a0841e4dd3e9057e4cf2ed44 Mon Sep 17 00:00:00 2001 From: LRFLEW Date: Thu, 2 Jun 2016 03:46:01 -0500 Subject: [PATCH 3/4] Updated Xcode Project (#14) --- OpenRCT2.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 6726611652..46840c06ed 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 007A05CD1CFB2C8B00F419C3 /* NetworkAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A05C01CFB2C8B00F419C3 /* NetworkAction.cpp */; }; - 007A05CE1CFB2C8B00F419C3 /* NetworkAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A05C21CFB2C8B00F419C3 /* NetworkAddress.cpp */; }; 007A05CF1CFB2C8B00F419C3 /* NetworkConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A05C41CFB2C8B00F419C3 /* NetworkConnection.cpp */; }; 007A05D01CFB2C8B00F419C3 /* NetworkGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A05C61CFB2C8B00F419C3 /* NetworkGroup.cpp */; }; 007A05D11CFB2C8B00F419C3 /* NetworkPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A05C81CFB2C8B00F419C3 /* NetworkPacket.cpp */; }; @@ -310,6 +309,7 @@ D45A395E1CF300AF00659A24 /* libSDL2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B81CF3006400659A24 /* libSDL2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D45A395F1CF300AF00659A24 /* libspeexdsp.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B91CF3006400659A24 /* libspeexdsp.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D47304D51C4FF8250015C0EA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D47304D41C4FF8250015C0EA /* libz.tbd */; }; + D48A8D831D00272F00649DA7 /* TcpSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D48A8D811D00272F00649DA7 /* TcpSocket.cpp */; }; D4EC48E61C2637710024B507 /* g2.dat in Resources */ = {isa = PBXBuildFile; fileRef = D4EC48E31C2637710024B507 /* g2.dat */; }; D4EC48E71C2637710024B507 /* language in Resources */ = {isa = PBXBuildFile; fileRef = D4EC48E41C2637710024B507 /* language */; }; D4EC48E81C2637710024B507 /* title in Resources */ = {isa = PBXBuildFile; fileRef = D4EC48E51C2637710024B507 /* title */; }; @@ -339,8 +339,6 @@ /* Begin PBXFileReference section */ 007A05C01CFB2C8B00F419C3 /* NetworkAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkAction.cpp; sourceTree = ""; }; 007A05C11CFB2C8B00F419C3 /* NetworkAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkAction.h; sourceTree = ""; }; - 007A05C21CFB2C8B00F419C3 /* NetworkAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkAddress.cpp; sourceTree = ""; }; - 007A05C31CFB2C8B00F419C3 /* NetworkAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkAddress.h; sourceTree = ""; }; 007A05C41CFB2C8B00F419C3 /* NetworkConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkConnection.cpp; sourceTree = ""; }; 007A05C51CFB2C8B00F419C3 /* NetworkConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkConnection.h; sourceTree = ""; }; 007A05C61CFB2C8B00F419C3 /* NetworkGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkGroup.cpp; sourceTree = ""; }; @@ -901,6 +899,8 @@ D45A39581CF3007A00659A24 /* speexdsp_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = speexdsp_types.h; sourceTree = ""; }; D47304D41C4FF8250015C0EA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; D4895D321C23EFDD000CD788 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = distribution/osx/Info.plist; sourceTree = SOURCE_ROOT; }; + D48A8D811D00272F00649DA7 /* TcpSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TcpSocket.cpp; sourceTree = ""; }; + D48A8D821D00272F00649DA7 /* TcpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TcpSocket.h; sourceTree = ""; }; D497D0781C20FD52002BF46A /* OpenRCT2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenRCT2.app; sourceTree = BUILT_PRODUCTS_DIR; }; D4EC48E31C2637710024B507 /* g2.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = g2.dat; path = data/g2.dat; sourceTree = SOURCE_ROOT; }; D4EC48E41C2637710024B507 /* language */ = {isa = PBXFileReference; lastKnownFileType = folder; name = language; path = data/language; sourceTree = SOURCE_ROOT; }; @@ -1347,8 +1347,6 @@ children = ( 007A05C01CFB2C8B00F419C3 /* NetworkAction.cpp */, 007A05C11CFB2C8B00F419C3 /* NetworkAction.h */, - 007A05C21CFB2C8B00F419C3 /* NetworkAddress.cpp */, - 007A05C31CFB2C8B00F419C3 /* NetworkAddress.h */, 007A05C41CFB2C8B00F419C3 /* NetworkConnection.cpp */, 007A05C51CFB2C8B00F419C3 /* NetworkConnection.h */, 007A05C61CFB2C8B00F419C3 /* NetworkGroup.cpp */, @@ -1366,6 +1364,8 @@ 00EFEE711CF1D80B0035213B /* NetworkKey.h */, C61FB7221CF86356004CE991 /* NetworkUser.cpp */, C61FB7231CF86356004CE991 /* NetworkUser.h */, + D48A8D811D00272F00649DA7 /* TcpSocket.cpp */, + D48A8D821D00272F00649DA7 /* TcpSocket.h */, D44271541CC81B3200D84D28 /* twitch.cpp */, D44271551CC81B3200D84D28 /* twitch.h */, ); @@ -1992,7 +1992,6 @@ C686F91D1CDBC3B7009F9BFC /* multi_dimension_roller_coaster.c in Sources */, C686F8B31CDBC37E009F9BFC /* surface.c in Sources */, D442724E1CC81B3200D84D28 /* scenario_sources.c in Sources */, - 007A05CE1CFB2C8B00F419C3 /* NetworkAddress.cpp in Sources */, D442729A1CC81B3200D84D28 /* banner.c in Sources */, C650B21C1CCABC4400B4D91C /* ConvertCommand.cpp in Sources */, D44272211CC81B3200D84D28 /* viewport_interaction.c in Sources */, @@ -2243,6 +2242,7 @@ D44272601CC81B3200D84D28 /* error.c in Sources */, C686F8B41CDBC37E009F9BFC /* paint.c in Sources */, D442728D1CC81B3200D84D28 /* title_exit.c in Sources */, + D48A8D831D00272F00649DA7 /* TcpSocket.cpp in Sources */, D442722C1CC81B3200D84D28 /* user.c in Sources */, C686F92F1CDBC3B7009F9BFC /* ferris_wheel.c in Sources */, D44272301CC81B3200D84D28 /* marketing.c in Sources */, From a153d076374905bd07f724cac4efcdba6f84c8e7 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 2 Jun 2016 18:50:37 +0100 Subject: [PATCH 4/4] handle exceptions when setting up the TCP listener --- src/network/network.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 335d795f0d..9966c8a95e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -210,6 +210,8 @@ bool Network::BeginClient(const char* host, unsigned short port) if (!Init()) return false; + mode = NETWORK_MODE_CLIENT; + assert(server_connection.Socket == nullptr); server_connection.Socket = CreateTcpSocket(); server_connection.Socket->ConnectAsync(host, port); @@ -218,7 +220,6 @@ bool Network::BeginClient(const char* host, unsigned short port) BeginChatLog(); - mode = NETWORK_MODE_CLIENT; utf8 keyPath[MAX_PATH]; network_get_private_key_path(keyPath, sizeof(keyPath), gConfigNetwork.player_name); if (!platform_file_exists(keyPath)) { @@ -278,13 +279,24 @@ bool Network::BeginServer(unsigned short port, const char* address) if (!Init()) return false; + mode = NETWORK_MODE_SERVER; + _userManager.Load(); log_verbose("Begin listening for clients"); assert(listening_socket == nullptr); listening_socket = CreateTcpSocket(); - listening_socket->Listen(address, port); + try + { + listening_socket->Listen(address, port); + } + catch (Exception ex) + { + Console::Error::WriteLine(ex.GetMsg()); + Close(); + return false; + } ServerName = gConfigNetwork.server_name; ServerDescription = gConfigNetwork.server_description; @@ -304,7 +316,6 @@ bool Network::BeginServer(unsigned short port, const char* address) printf("Ready for clients...\n"); network_chat_show_connected_message(); - mode = NETWORK_MODE_SERVER; status = NETWORK_STATUS_CONNECTED; listening_port = port; advertise_status = ADVERTISE_STATUS_DISABLED;