Merge branch 'master' into jgrpp-beta

# Conflicts:
#	src/engine_base.h
#	src/gfxinit.cpp
#	src/graph_gui.cpp
#	src/lang/brazilian_portuguese.txt
#	src/lang/dutch.txt
#	src/lang/french.txt
#	src/lang/korean.txt
#	src/lang/norwegian_bokmal.txt
#	src/lang/portuguese.txt
#	src/lang/russian.txt
#	src/lang/spanish.txt
#	src/lang/spanish_MX.txt
#	src/network/core/address.cpp
#	src/network/core/game_info.h
#	src/network/core/os_abstraction.h
#	src/network/core/udp.cpp
#	src/network/network_client.cpp
#	src/network/network_client.h
#	src/network/network_internal.h
#	src/newgrf_engine.cpp
#	src/settings_gui.cpp
#	src/station_cmd.cpp
#	src/string_func.h
#	src/town_gui.cpp
#	src/video/video_driver.cpp
#	src/widget_type.h
This commit is contained in:
Jonathan G Rennison
2021-09-18 01:28:57 +01:00
50 changed files with 974 additions and 527 deletions

View File

@@ -8,6 +8,7 @@ add_files(
game_info.h
host.cpp
host.h
os_abstraction.cpp
os_abstraction.h
packet.cpp
packet.h

View File

@@ -94,6 +94,18 @@ void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool wit
}
}
/**
* Get the address as a string, e.g. 127.0.0.1:12345.
* @param with_family whether to add the family (e.g. IPvX).
* @return the address
*/
std::string NetworkAddress::GetAddressAsString(bool with_family)
{
char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
this->GetAddressAsString(buf, lastof(buf), with_family);
return buf;
}
/**
* Get the address as a string, e.g. 127.0.0.1:12345.
* @param with_family whether to add the family (e.g. IPvX).
@@ -311,12 +323,11 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
{
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
char address[NETWORK_HOSTNAME_LENGTH + 6 + 7];
NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address));
std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
if (sock == INVALID_SOCKET) {
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkGetLastErrorString());
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString());
return INVALID_SOCKET;
}
@@ -325,8 +336,8 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type);
int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen);
if (err != 0 && NetworkGetLastError() != EINPROGRESS) {
DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString());
if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) {
DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
@@ -342,28 +353,28 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS;
int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv);
if (n < 0) {
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetLastErrorString());
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
/* If no fd is selected, the timeout has been reached. */
if (n == 0) {
DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address);
DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str());
closesocket(sock);
return INVALID_SOCKET;
}
/* Retrieve last error, if any, on the socket. */
err = GetSocketError(sock);
if (err != 0) {
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetErrorString(err));
NetworkError socket_error = GetSocketError(sock);
if (socket_error.HasError()) {
DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), socket_error.AsString());
closesocket(sock);
return INVALID_SOCKET;
}
/* Connection succeeded. */
DEBUG(net, 1, "[%s] connected to %s", type, address);
DEBUG(net, 1, "[%s] connected to %s", type, address.c_str());
return sock;
}
@@ -388,48 +399,47 @@ static SOCKET ListenLoopProc(addrinfo *runp)
{
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
char address[NETWORK_HOSTNAME_LENGTH + 6 + 7];
NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address));
std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
if (sock == INVALID_SOCKET) {
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkGetLastErrorString());
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
return INVALID_SOCKET;
}
if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address);
DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address.c_str());
}
int on = 1;
/* The (const char*) cast is needed for windows!! */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) {
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkGetLastErrorString());
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
}
#ifndef __OS2__
if (runp->ai_family == AF_INET6 &&
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) {
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkGetLastErrorString());
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkError::GetLast().AsString());
}
#endif
if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkGetLastErrorString());
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkGetLastErrorString());
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
/* Connection succeeded */
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address);
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address.c_str());
DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address);
DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address.c_str());
return sock;
}

View File

@@ -190,8 +190,8 @@ struct NetworkAddressDumper {
const char *GetAddressAsString(NetworkAddress *addr, bool with_family = true);
private:
/* 6 = for the : and 5 for the decimal port number */
char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
/* 7 extra are for with_family, which adds " (IPvX)". */
char buf[NETWORK_HOSTNAME_PORT_LENGTH + 7];
};
#endif /* NETWORK_CORE_ADDRESS_H */

View File

@@ -57,6 +57,7 @@ static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What vers
static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'
static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536)
static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0'
static const uint NETWORK_LONG_REVISION_LENGTH = 64; ///< The maximum length of the revision, in bytes including '\0'

View File

@@ -13,7 +13,6 @@
#include "../../debug.h"
#include "os_abstraction.h"
#include "packet.h"
#include "../../string_func.h"
#include "../../safeguards.h"
@@ -48,20 +47,3 @@ void NetworkCoreShutdown()
WSACleanup();
#endif
}
#if defined(_WIN32)
/**
* Return the string representation of the given error from the OS's network functions.
* @param error The error number (from \c NetworkGetLastError()).
* @return The error message, potentially an empty string but never \c nullptr.
*/
const char *NetworkGetErrorString(int error)
{
static char buffer[512];
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
seprintf(buffer, lastof(buffer), "Unknown error %d", error);
}
return buffer;
}
#endif /* defined(_WIN32) */

View File

@@ -74,7 +74,6 @@ struct NetworkGameInfo : NetworkServerGameInfo {
uint32 map_width; ///< Map width
uint32 map_height; ///< Map height
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any)
char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated)
char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
bool dedicated; ///< Is this a dedicated server?

View File

@@ -0,0 +1,217 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff.
*
* The general idea is to have simple abstracting functions for things that
* require different implementations for different environments.
* In here the functions, and their documentation, are defined only once
* and the implementation contains the #ifdefs to change the implementation.
* Since Windows is usually different that is usually the first case, after
* that the behaviour is usually Unix/BSD-like with occasional variation.
*/
#include "stdafx.h"
#include "os_abstraction.h"
#include "../../string_func.h"
#include <mutex>
#include "../../safeguards.h"
/**
* Construct the network error with the given error code.
* @param error The error code.
*/
NetworkError::NetworkError(int error) : error(error)
{
}
/**
* Check whether this error describes that the operation would block.
* @return True iff the operation would block.
*/
bool NetworkError::WouldBlock() const
{
#if defined(_WIN32)
return this->error == WSAEWOULDBLOCK;
#else
/* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not
* and the POSIX.1 specification states that either should be checked. */
return this->error == EWOULDBLOCK || this->error == EAGAIN;
#endif
}
/**
* Check whether this error describes a connection reset.
* @return True iff the connection is reset.
*/
bool NetworkError::IsConnectionReset() const
{
#if defined(_WIN32)
return this->error == WSAECONNRESET;
#else
return this->error == ECONNRESET;
#endif
}
/**
* Check whether this error describes a connect is in progress.
* @return True iff the connect is already in progress.
*/
bool NetworkError::IsConnectInProgress() const
{
#if defined(_WIN32)
return this->error == WSAEWOULDBLOCK;
#else
return this->error == EINPROGRESS;
#endif
}
/**
* Get the string representation of the error message.
* @return The string representation that will get overwritten by next calls.
*/
const char *NetworkError::AsString() const
{
if (this->message.empty()) {
#if defined(_WIN32)
char buffer[512];
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
seprintf(buffer, lastof(buffer), "Unknown error %d", this->error);
}
this->message.assign(buffer);
#else
/* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however
* the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable.
* The problem with the non-POSIX variant is that it does not necessarily fill the buffer with
* the error message but can also return a pointer to a static bit of memory, whereas the POSIX
* variant always fills the buffer. This makes the behaviour too erratic to work with. */
static std::mutex mutex;
std::lock_guard<std::mutex> guard(mutex);
this->message.assign(strerror(this->error));
#endif
}
return this->message.c_str();
}
/**
* Check whether an error was actually set.
* @return True iff an error was set.
*/
bool NetworkError::HasError() const
{
return this->error != 0;
}
/**
* Get the last network error.
* @return The network error.
*/
/* static */ NetworkError NetworkError::GetLast()
{
#if defined(_WIN32)
return NetworkError(WSAGetLastError());
#elif defined(__OS2__)
return NetworkError(sock_errno());
#else
return NetworkError(errno);
#endif
}
/**
* Try to set the socket into non-blocking mode.
* @param d The socket to set the non-blocking more for.
* @return True if setting the non-blocking mode succeeded, otherwise false.
*/
bool SetNonBlocking(SOCKET d)
{
#if defined(_WIN32)
u_long nonblocking = 1;
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#elif defined __EMSCRIPTEN__
return true;
#else
int nonblocking = 1;
return ioctl(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
* Try to set the socket into blocking mode.
* @param d The socket to set the blocking more for.
* @return True if setting the blocking mode succeeded, otherwise false.
*/
bool SetBlocking(SOCKET d)
{
#if defined(_WIN32)
u_long nonblocking = 0;
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#elif defined __EMSCRIPTEN__
return true;
#else
int nonblocking = 0;
return ioctl(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
* Try to set the socket to not delay sending.
* @param d The socket to disable the delaying for.
* @return True if disabling the delaying succeeded, otherwise false.
*/
bool SetNoDelay(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
int flags = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0;
#endif
}
/**
* Try to shutdown the socket in one or both directions.
* @param d The socket to disable the delaying for.
* @param read Whether to shutdown the read direction.
* @param write Whether to shutdown the write direction.
* @param linger_timeout The socket linger timeout.
* @return True if successful
*/
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
{
if (!read && !write) return true;
#ifdef _WIN32
LINGER ln = { 1U, (uint16) linger_timeout };
#else
struct linger ln = { 1, (int) linger_timeout };
#endif
setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
int how = SD_BOTH;
if (!read) how = SD_SEND;
if (!write) how = SD_RECEIVE;
return shutdown(d, how) == 0;
}
/**
* Get the error from a socket, if any.
* @param d The socket to get the error from.
* @return The errno on the socket.
*/
NetworkError GetSocketError(SOCKET d)
{
int err;
socklen_t len = sizeof(err);
getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
return NetworkError(err);
}

View File

@@ -14,6 +14,26 @@
#ifndef NETWORK_CORE_OS_ABSTRACTION_H
#define NETWORK_CORE_OS_ABSTRACTION_H
/**
* Abstraction of a network error where all implementation details of the
* error codes are encapsulated in this class and the abstraction layer.
*/
class NetworkError {
private:
int error; ///< The underlying error number from errno or WSAGetLastError.
mutable std::string message; ///< The string representation of the error (set on first call to #AsString).
public:
NetworkError(int error);
bool HasError() const;
bool WouldBlock() const;
bool IsConnectionReset() const;
bool IsConnectInProgress() const;
const char *AsString() const;
static NetworkError GetLast();
};
/* Include standard stuff per OS */
/* Windows stuff */
@@ -23,21 +43,6 @@
#include <ws2tcpip.h>
#include <windows.h>
/**
* Get the last error code from any of the OS's network functions.
* What it returns and when it is reset, is implementation defined.
* @return The last error code.
*/
#define NetworkGetLastError() WSAGetLastError()
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#undef ECONNRESET
#define ECONNRESET WSAECONNRESET
#undef EINPROGRESS
#define EINPROGRESS WSAEWOULDBLOCK
const char *NetworkGetErrorString(int error);
/* Windows has some different names for some types */
typedef unsigned long in_addr_t;
@@ -74,10 +79,7 @@ typedef unsigned long in_addr_t;
# endif
# define SOCKET int
# define INVALID_SOCKET -1
# define ioctlsocket ioctl
# define closesocket close
# define NetworkGetLastError() (errno)
# define NetworkGetErrorString(error) (strerror(error))
# define SD_RECEIVE SHUT_RD
# define SD_SEND SHUT_WR
# define SD_BOTH SHUT_RDWR
@@ -128,10 +130,7 @@ typedef unsigned long in_addr_t;
#if defined(__OS2__)
# define SOCKET int
# define INVALID_SOCKET -1
# define ioctlsocket ioctl
# define closesocket close
# define NetworkGetLastError() (sock_errno())
# define NetworkGetErrorString(error) (strerror(error))
# define SD_RECEIVE SHUT_RD
# define SD_SEND SHUT_WR
# define SD_BOTH SHUT_RDWR
@@ -206,105 +205,11 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address
}
#endif
/**
* Return the string representation of the last error from the OS's network functions.
* @return The error message, potentially an empty string but never \c nullptr.
*/
static inline const char *NetworkGetLastErrorString()
{
return NetworkGetErrorString(NetworkGetLastError());
}
/**
* Try to set the socket into non-blocking mode.
* @param d The socket to set the non-blocking more for.
* @return True if setting the non-blocking mode succeeded, otherwise false.
*/
static inline bool SetNonBlocking(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
# ifdef _WIN32
u_long nonblocking = 1;
# else
int nonblocking = 1;
# endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
* Try to set the socket into blocking mode.
* @param d The socket to set the blocking more for.
* @return True if setting the blocking mode succeeded, otherwise false.
*/
static inline bool SetBlocking(SOCKET d)
{
#ifdef _WIN32
u_long nonblocking = 0;
#else
int nonblocking = 0;
#endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
}
/**
* Try to set the socket to not delay sending.
* @param d The socket to disable the delaying for.
* @return True if disabling the delaying succeeded, otherwise false.
*/
static inline bool SetNoDelay(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
/* XXX should this be done at all? */
int b = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
#endif
}
/**
* Try to shutdown the socket in one or both directions.
* @param d The socket to disable the delaying for.
* @param read Whether to shutdown the read direction.
* @param write Whether to shutdown the write direction.
* @param linger_timeout The socket linger timeout.
* @return True if successful
*/
static inline bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
{
if (!read && !write) return true;
#ifdef _WIN32
LINGER ln = { 1U, (uint16) linger_timeout };
#else
struct linger ln = { 1, (int) linger_timeout };
#endif
setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
int how = SD_BOTH;
if (!read) how = SD_SEND;
if (!write) how = SD_RECEIVE;
return shutdown(d, how) == 0;
}
/**
* Get the error from a socket, if any.
* @param d The socket to get the error from.
* @return The errno on the socket.
*/
static inline int GetSocketError(SOCKET d)
{
int err;
socklen_t len = sizeof(err);
getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
return err;
}
bool SetNonBlocking(SOCKET d);
bool SetBlocking(SOCKET d);
bool SetNoDelay(SOCKET d);
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout);
NetworkError GetSocketError(SOCKET d);
/* Make sure these structures have the size we expect them to be */
static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes.

View File

@@ -114,11 +114,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
Packet *p = this->packet_queue.front().get();
res = p->TransferOut<int>(send, this->sock, 0);
if (res == -1) {
int err = NetworkGetLastError();
if (err != EWOULDBLOCK) {
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong.. close client! */
if (!closing_down) {
DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err));
DEBUG(net, 0, "send failed with error %s", err.AsString());
this->CloseConnection();
}
return SPS_CLOSED;
@@ -165,10 +165,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
while (p->RemainingBytesToTransfer() != 0) {
res = p->TransferIn<int>(recv, this->sock, 0);
if (res == -1) {
int err = NetworkGetLastError();
if (err != EWOULDBLOCK) {
/* Something went wrong... (ECONNRESET is connection reset by peer) */
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
this->CloseConnection();
return nullptr;
}
@@ -194,10 +194,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
while (p->RemainingBytesToTransfer() != 0) {
res = p->TransferIn<int>(recv, this->sock, 0);
if (res == -1) {
int err = NetworkGetLastError();
if (err != EWOULDBLOCK) {
/* Something went wrong... (ECONNRESET is connection reset by peer) */
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
this->CloseConnection();
return nullptr;
}

View File

@@ -27,8 +27,8 @@ static const char* _packet_game_type_names[] {
"SERVER_ERROR",
"CLIENT_COMPANY_INFO",
"SERVER_COMPANY_INFO",
"CLIENT_GAME_INFO",
"SERVER_GAME_INFO",
"CLIENT_GAME_INFO",
"SERVER_GAME_INFO_EXTENDED",
"SERVER_CHECK_NEWGRFS",
"CLIENT_NEWGRFS_CHECKED",

View File

@@ -44,8 +44,8 @@ enum PacketGameType {
PACKET_SERVER_COMPANY_INFO, ///< Information about a single company.
/* Packets used to get the game info. */
PACKET_CLIENT_GAME_INFO, ///< Request information about the server.
PACKET_SERVER_GAME_INFO, ///< Information about the server.
PACKET_CLIENT_GAME_INFO, ///< Request information about the server.
/*
* Packets after here assume that the client

View File

@@ -12,7 +12,7 @@
#include "../../stdafx.h"
#include "../../debug.h"
#include "../../rev.h"
#include "../network_func.h"
#include "../network_internal.h"
#include "game_info.h"
#include "tcp_http.h"
@@ -203,11 +203,7 @@ int NetworkHTTPSocketHandler::HandleHeader()
*url = '\0';
/* Fetch the hostname, and possible port number. */
const char *port = nullptr;
ParseConnectionString(&port, hname);
NetworkAddress address(hname, port == nullptr ? 80 : atoi(port));
NetworkAddress address = ParseConnectionString(hname, 80);
/* Restore the URL. */
*url = '/';
@@ -229,10 +225,10 @@ int NetworkHTTPSocketHandler::Receive()
for (;;) {
ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0);
if (res == -1) {
int err = NetworkGetLastError();
if (err != EWOULDBLOCK) {
/* Something went wrong... (ECONNRESET is connection reset by peer) */
if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err));
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
return -1;
}
/* Connection would block, so stop for now */

View File

@@ -64,7 +64,7 @@ public:
DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str());
if (p.TransferOut<int>(send, s, 0) < 0) {
DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString());
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
}
closesocket(s);
break;
@@ -81,7 +81,7 @@ public:
p.PrepareToSend();
if (p.TransferOut<int>(send, s, 0) < 0) {
DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString());
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
}
closesocket(s);
@@ -151,7 +151,7 @@ public:
if (sockets.size() == 0) {
DEBUG(net, 0, "[server] could not start network: could not create listening socket");
NetworkError(STR_NETWORK_ERROR_SERVER_START);
ShowNetworkError(STR_NETWORK_ERROR_SERVER_START);
return false;
}

View File

@@ -127,7 +127,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
/* Enable broadcast */
unsigned long val = 1;
if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkGetLastErrorString());
DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString());
}
}
@@ -136,7 +136,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
DEBUG(net, 7, "[udp] sendto(%s)", NetworkAddressDumper().GetAddressAsString(&send));
/* Check for any errors, but ignore it otherwise */
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkGetLastErrorString());
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkError::GetLast().AsString());
if (!all) break;
}

View File

@@ -306,7 +306,7 @@ uint NetworkCalculateLag(const NetworkClientSocket *cs)
/* There was a non-recoverable error, drop back to the main menu with a nice
* error */
void NetworkError(StringID error_string)
void ShowNetworkError(StringID error_string)
{
_switch_mode = SM_MENU;
ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL);
@@ -475,45 +475,15 @@ static void CheckPauseOnJoin()
CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN);
}
/**
* Converts a string to ip/port
* Format: IP:port
*
* connection_string will be re-terminated to separate out the hostname, port will
* be set to the port strings given by the user, inside the memory area originally
* occupied by connection_string.
*/
void ParseConnectionString(const char **port, char *connection_string)
{
bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
for (char *p = connection_string; *p != '\0'; p++) {
switch (*p) {
case '[':
ipv6 = true;
break;
case ']':
ipv6 = false;
break;
case ':':
if (ipv6) break;
*port = p + 1;
*p = '\0';
break;
}
}
}
/**
* Converts a string to ip/port/company
* Format: IP:port#company
*
* connection_string will be re-terminated to separate out the hostname, and company and port will
* be set to the company and port strings given by the user, inside the memory area originally
* occupied by connection_string.
* connection_string will be re-terminated to separate out the hostname, port will
* be set to the port strings given by the user, inside the memory area originally
* occupied by connection_string. Similar for company, if set.
*/
void ParseGameConnectionString(const char **company, const char **port, char *connection_string)
void ParseFullConnectionString(const char **company, const char **port, char *connection_string)
{
bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
for (char *p = connection_string; *p != '\0'; p++) {
@@ -527,6 +497,7 @@ void ParseGameConnectionString(const char **company, const char **port, char *co
break;
case '#':
if (company == nullptr) continue;
*company = p + 1;
*p = '\0';
break;
@@ -540,6 +511,51 @@ void ParseGameConnectionString(const char **company, const char **port, char *co
}
}
/**
* Convert a string containing either "hostname" or "hostname:ip" to a
* NetworkAddress.
*
* @param connection_string The string to parse.
* @param default_port The default port to set port to if not in connection_string.
* @return A valid NetworkAddress of the parsed information.
*/
NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port)
{
char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH];
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string));
const char *port = nullptr;
ParseFullConnectionString(nullptr, &port, internal_connection_string);
int rport = port != nullptr ? atoi(port) : default_port;
return NetworkAddress(internal_connection_string, rport);
}
/**
* Convert a string containing either "hostname" or "hostname:ip" to a
* NetworkAddress, where the string can be postfixed with "#company" to
* indicate the requested company.
*
* @param company Pointer to the company variable to set iff indicted.
* @param connection_string The string to parse.
* @param default_port The default port to set port to if not in connection_string.
* @return A valid NetworkAddress of the parsed information.
*/
NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port)
{
char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string));
const char *port_s = nullptr;
const char *company_s = nullptr;
ParseFullConnectionString(&company_s, &port_s, internal_connection_string);
if (company_s != nullptr) *company = (CompanyID)atoi(company_s);
int port = port_s != nullptr ? atoi(port_s) : default_port;
return NetworkAddress(internal_connection_string, port);
}
/**
* Handle the accepting of a connection to the server.
* @param s The socket of the new connection.
@@ -619,8 +635,11 @@ static void NetworkInitialize(bool close_admins = true)
/** Non blocking connection create to query servers */
class TCPQueryConnecter : TCPConnecter {
private:
bool request_company_info;
public:
TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {}
void OnFailure() override
{
@@ -630,45 +649,53 @@ public:
void OnConnect(SOCKET s) override
{
_networking = true;
new ClientNetworkGameSocketHandler(s);
MyClient::SendInformationQuery();
new ClientNetworkGameSocketHandler(s, address);
MyClient::SendInformationQuery(request_company_info);
}
};
/**
* Query a server to fetch his game-info.
* @param address the address to query.
* @param request_company_info Whether to request company info too.
*/
void NetworkTCPQueryServer(NetworkAddress address)
void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info)
{
if (!_network_available) return;
NetworkDisconnect();
NetworkInitialize();
new TCPQueryConnecter(address);
new TCPQueryConnecter(address, request_company_info);
}
/* Validates an address entered as a string and adds the server to
/**
* Validates an address entered as a string and adds the server to
* the list. If you use this function, the games will be marked
* as manually added. */
void NetworkAddServer(const char *b)
* as manually added.
* @param connection_string The IP:port of the server to add.
* @return The entry on the game list.
*/
NetworkGameList *NetworkAddServer(const std::string &connection_string)
{
if (*b != '\0') {
const char *port = nullptr;
char host[NETWORK_HOSTNAME_LENGTH];
uint16 rport;
if (connection_string.empty()) return nullptr;
strecpy(host, b, lastof(host));
NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT);
strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
rport = NETWORK_DEFAULT_PORT;
/* Ensure the item already exists in the list */
NetworkGameList *item = NetworkGameListAddItem(address);
if (StrEmpty(item->info.server_name)) {
ClearGRFConfigList(&item->info.grfconfig);
address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name));
item->manually = true;
ParseConnectionString(&port, host);
if (port != nullptr) rport = atoi(port);
NetworkUDPQueryServer(NetworkAddress(host, rport), true);
NetworkRebuildHostList();
UpdateNetworkGameWindow();
}
NetworkTCPQueryServer(address);
return item;
}
/**
@@ -707,41 +734,99 @@ public:
void OnFailure() override
{
NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION);
}
void OnConnect(SOCKET s) override
{
_networking = true;
new ClientNetworkGameSocketHandler(s);
new ClientNetworkGameSocketHandler(s, this->address);
IConsoleCmdExec("exec scripts/on_client.scr 0");
NetworkClient_Connected();
}
};
/* Used by clients, to connect to a server */
void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password)
/**
* Join a client to the server at with the given connection string.
* The default for the passwords is \c nullptr. When the server or company needs a
* password and none is given, the user is asked to enter the password in the GUI.
* This function will return false whenever some information required to join is not
* correct such as the company number or the client's name, or when there is not
* networking avalabile at all. If the function returns false the connection with
* the existing server is not disconnected.
* It will return true when it starts the actual join process, i.e. when it
* actually shows the join status window.
*
* @param connection_string The IP address, port and company number to join as.
* @param default_company The company number to join as when none is given.
* @param join_server_password The password for the server.
* @param join_company_password The password for the company.
* @return Whether the join has started.
*/
bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password)
{
if (!_network_available) return;
CompanyID join_as = default_company;
NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT);
if (port == 0) return;
if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) {
join_as--;
if (join_as >= MAX_COMPANIES) {
return false;
}
}
if (!NetworkValidateClientName()) return;
return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password);
}
strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host));
_settings_client.network.last_port = port;
_network_join_as = join_as;
_network_join_server_password = join_server_password;
_network_join_company_password = join_company_password;
/**
* Join a client to the server at the given address.
* See the overloaded NetworkClientConnectGame for more details.
*
* @param address The network address of the server to join to.
* @param join_as The company number to join as.
* @param join_server_password The password for the server.
* @param join_company_password The password for the company.
* @return Whether the join has started.
*/
bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password)
{
if (!_network_available) return false;
if (!NetworkValidateClientName()) return false;
_network_join.address = address;
_network_join.company = join_as;
_network_join.server_password = join_server_password;
_network_join.company_password = join_company_password;
if (_game_mode == GM_MENU) {
/* From the menu we can immediately continue with the actual join. */
NetworkClientJoinGame();
} else {
/* When already playing a game, first go back to the main menu. This
* disconnects the user from the current game, meaning we can safely
* load in the new. After all, there is little point in continueing to
* play on a server if we are connecting to another one.
*/
_switch_mode = SM_JOIN_GAME;
}
return true;
}
/**
* Actually perform the joining to the server. Use #NetworkClientConnectGame
* when you want to connect to a specific server/company. This function
* assumes _network_join is already fully set up.
*/
void NetworkClientJoinGame()
{
NetworkDisconnect();
NetworkInitialize();
strecpy(_settings_client.network.last_joined, _network_join.address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined));
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
ShowJoinStatusWindow();
new TCPClientConnecter(NetworkAddress(hostname, port));
new TCPClientConnecter(_network_join.address);
}
static void NetworkInitGameInfo()
@@ -1126,13 +1211,14 @@ static void NetworkGenerateServerId()
seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output);
}
void NetworkStartDebugLog(const char *hostname, uint16 port)
void NetworkStartDebugLog(const std::string &connection_string)
{
extern SOCKET _debug_socket; // Comes from debug.c
DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port);
NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT);
DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str());
NetworkAddress address(hostname, port);
SOCKET s = address.Connect();
if (s == INVALID_SOCKET) {
DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
@@ -1180,9 +1266,9 @@ void NetworkShutDown()
#ifdef __EMSCRIPTEN__
extern "C" {
void CDECL em_openttd_add_server(const char *host, int port)
void CDECL em_openttd_add_server(const char *connection_string)
{
NetworkUDPQueryServer(NetworkAddress(host, port), true);
NetworkAddServer(connection_string);
}
}

View File

@@ -150,9 +150,9 @@ void ClientNetworkEmergencySave()
* Create a new socket for the client side of the game connection.
* @param s The socket to connect with.
*/
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s)
: NetworkGameSocketHandler (s),
savegame (nullptr), token (0), status (STATUS_INACTIVE)
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, NetworkAddress address)
: NetworkGameSocketHandler(s),
address(address), savegame(nullptr), token(0), status(STATUS_INACTIVE)
{
assert(ClientNetworkGameSocketHandler::my_client == nullptr);
ClientNetworkGameSocketHandler::my_client = this;
@@ -324,7 +324,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
#endif
if (_sync_state_checksum != _state_checksum.state) info.flags |= DesyncExtraInfo::DEIF_STATE;
NetworkError(STR_NETWORK_ERROR_DESYNC);
ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x} {%x, " OTTD_PRINTFHEX64 "} != {%x, " OTTD_PRINTFHEX64 "}"
, _date, _date_fract, _tick_skip_counter, _sync_seed_1, _sync_state_checksum, _random.state[0], _state_checksum.state);
DEBUG(net, 0, "Sync error detected!");
@@ -389,13 +389,8 @@ static uint8 _network_server_max_companies;
/** Maximum number of spectators of the currently joined server. */
static uint8 _network_server_max_spectators;
/** Who would we like to join as. */
CompanyID _network_join_as;
/** Login password from -p argument */
const char *_network_join_server_password = nullptr;
/** Company password from -P argument */
const char *_network_join_company_password = nullptr;
/** Information about the game to join to. */
NetworkJoinInfo _network_join;
/** Make sure the server ID length is the same as a md5 hash. */
static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
@@ -408,11 +403,9 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
/**
* Query the server for server information.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery()
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info)
{
my_client->status = STATUS_COMPANY_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
my_client->status = STATUS_GAME_INFO;
Packet *p = new Packet(PACKET_CLIENT_GAME_INFO);
p->Send_uint32(FIND_SERVER_EXTENDED_TOKEN);
@@ -421,7 +414,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery()
p->Send_uint16(0); // version
my_client->SendPacket(p);
my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO));
if (request_company_info) {
my_client->status = STATUS_COMPANY_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO));
}
return NETWORK_RECV_STATUS_OKAY;
}
@@ -437,7 +436,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin()
p->Send_string(_openttd_revision);
p->Send_uint32(_openttd_newgrf_version);
p->Send_string(_settings_client.network.client_name); // Client name
p->Send_uint8 (_network_join_as); // PlayAs
p->Send_uint8 (_network_join.company); // PlayAs
p->Send_uint8 (0); // Used to be language
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
@@ -698,9 +697,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p)
{
if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
NetworkGameList *item = GetLobbyGameInfo();
if (item == nullptr) {
/* This is not the lobby, so add it to the game list. */
item = NetworkGameListAddItem(this->address);
}
/* Clear any existing GRFConfig chain. */
ClearGRFConfigList(&item->info.grfconfig);
@@ -711,6 +714,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe
/* Ensure we consider the server online. */
item->online = true;
/* It could be either window, but only one is open, so redraw both. */
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
/* We will receive company info next, so keep connection open. */
@@ -870,6 +875,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p
NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8();
/* If we query a server that is 1.11.1 or older, we get an
* NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show a special
* error popup in that case.
*/
if (error == NETWORK_ERROR_NOT_EXPECTED && (this->status == STATUS_GAME_INFO || this->status == STATUS_COMPANY_INFO)) {
ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL);
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
StringID err = STR_NETWORK_ERROR_LOSTCONNECTION;
if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error];
/* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */
@@ -933,7 +947,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW
p->Recv_string(_password_server_id, sizeof(_password_server_id));
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
const char *password = _network_join_server_password;
const char *password = _network_join.server_password;
if (!StrEmpty(password)) {
return SendGamePassword(password);
}
@@ -952,7 +966,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PA
p->Recv_string(_password_server_id, sizeof(_password_server_id));
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
const char *password = _network_join_company_password;
const char *password = _network_join.company_password;
if (!StrEmpty(password)) {
return SendCompanyPassword(password);
}
@@ -1077,10 +1091,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
/* New company/spectator (invalid company) or company we want to join is not active
* Switch local company to spectator and await the server's judgement */
if (_network_join_as == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join_as)) {
if (_network_join.company == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join.company)) {
SetLocalCompany(COMPANY_SPECTATOR);
if (_network_join_as != COMPANY_SPECTATOR) {
if (_network_join.company != COMPANY_SPECTATOR) {
/* We have arrived and ready to start playing; send a command to make a new company;
* the server will give us a client-id and let us in */
_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
@@ -1089,7 +1103,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
}
} else {
/* take control over an existing company */
SetLocalCompany(_network_join_as);
SetLocalCompany(_network_join.company);
}
return NETWORK_RECV_STATUS_OKAY;
@@ -1420,6 +1434,7 @@ const char *ClientNetworkGameSocketHandler::GetServerStatusName(ServerStatus sta
{
static const char* _server_status_names[] {
"INACTIVE",
"GAME_INFO",
"COMPANY_INFO",
"JOIN",
"NEWGRFS_CHECK",

View File

@@ -15,12 +15,14 @@
/** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : public NetworkGameSocketHandler {
private:
NetworkAddress address; ///< Address we are connected to.
struct PacketReader *savegame; ///< Packet reader for reading the savegame.
byte token; ///< The token we need to send back to the server to prove we're the right client.
/** Status of the connection with the server. */
enum ServerStatus {
STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_GAME_INFO, ///< We are trying to get the game information.
STATUS_COMPANY_INFO, ///< We are trying to get company information.
STATUS_JOIN, ///< We are trying to join a server.
STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs.
@@ -84,7 +86,7 @@ protected:
static NetworkRecvStatus SendMapOk();
void CheckConnection();
public:
ClientNetworkGameSocketHandler(SOCKET s);
ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address);
~ClientNetworkGameSocketHandler();
NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override;
@@ -92,7 +94,7 @@ public:
std::string GetDebugInfo() const override;
static NetworkRecvStatus SendInformationQuery();
static NetworkRecvStatus SendInformationQuery(bool request_company_info);
static NetworkRecvStatus SendJoin();
static NetworkRecvStatus SendCommand(const CommandPacket *cp);
@@ -127,9 +129,15 @@ typedef ClientNetworkGameSocketHandler MyClient;
void NetworkClient_Connected();
void NetworkClientSetCompanyPassword(const char *password);
extern CompanyID _network_join_as;
/** Information required to join a server. */
struct NetworkJoinInfo {
NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {}
NetworkAddress address; ///< The address of the server to join.
CompanyID company; ///< The company to join.
const char *server_password; ///< The password of the server to join.
const char *company_password; ///< The password of the company to join.
};
extern const char *_network_join_server_password;
extern const char *_network_join_company_password;
extern NetworkJoinInfo _network_join;
#endif /* NETWORK_CLIENT_H */

View File

@@ -45,14 +45,14 @@ void NetworkReboot();
void NetworkDisconnect(bool blocking = false, bool close_admins = true);
void NetworkGameLoop();
void NetworkBackgroundLoop();
void ParseConnectionString(const char **port, char *connection_string);
void ParseGameConnectionString(const char **company, const char **port, char *connection_string);
void NetworkStartDebugLog(const char *hostname, uint16 port);
void ParseFullConnectionString(const char **company, const char **port, char *connection_string);
void NetworkStartDebugLog(const std::string &connection_string);
void NetworkPopulateCompanyStats(NetworkCompanyStats *stats);
void NetworkUpdateClientInfo(ClientID client_id);
void NetworkClientsToSpectators(CompanyID cid);
void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
void NetworkClientJoinGame();
void NetworkClientRequestMove(CompanyID company, const char *pass = "");
void NetworkClientSendRcon(const char *password, const char *command);
void NetworkClientSendSettingsPassword(const char *password);

View File

@@ -51,7 +51,6 @@ static void NetworkGameListHandleDelayedInsert()
ClearGRFConfigList(&item->info.grfconfig);
memset(&item->info, 0, sizeof(item->info));
strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name));
strecpy(item->info.hostname, ins_item->info.hostname, lastof(item->info.hostname));
item->online = false;
}
item->manually |= ins_item->manually;
@@ -70,15 +69,6 @@ static void NetworkGameListHandleDelayedInsert()
*/
NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
{
const char *hostname = address.GetHostname();
/* Do not query the 'any' address. */
if (StrEmpty(hostname) ||
strcmp(hostname, "0.0.0.0") == 0 ||
strcmp(hostname, "::") == 0) {
return nullptr;
}
NetworkGameList *item, *prev_item;
prev_item = nullptr;
@@ -96,7 +86,6 @@ NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
} else {
prev_item->next = item;
}
DEBUG(net, 4, "[gamelist] added server to list");
UpdateNetworkGameWindow();
@@ -152,7 +141,7 @@ void NetworkGameListRequery()
/* item gets mostly zeroed by NetworkUDPQueryServer */
uint8 retries = item->retries;
NetworkUDPQueryServer(NetworkAddress(item->address));
NetworkUDPQueryServer(item->address);
item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries;
}
}

View File

@@ -489,9 +489,8 @@ public:
EM_ASM(if (window["openttd_server_list"]) openttd_server_list());
#endif
this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
this->last_joined = NetworkAddServer(_settings_client.network.last_joined);
this->server = this->last_joined;
if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address);
this->requery_timer.SetInterval(MILLISECONDS_PER_TICK);
@@ -662,9 +661,8 @@ public:
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version
y += FONT_HEIGHT_NORMAL;
char network_addr_buffer[NETWORK_HOSTNAME_LENGTH + 6 + 7];
sel->address.GetAddressAsString(network_addr_buffer, lastof(network_addr_buffer));
SetDParamStr(0, network_addr_buffer);
std::string address = sel->address.GetAddressAsString();
SetDParamStr(0, address.c_str());
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address
y += FONT_HEIGHT_NORMAL;
@@ -753,7 +751,7 @@ public:
ShowQueryString(
STR_JUST_RAW_STRING,
STR_NETWORK_SERVER_LIST_ENTER_IP,
NETWORK_HOSTNAME_LENGTH, // maximum number of characters including '\0'
NETWORK_HOSTNAME_PORT_LENGTH, // maximum number of characters including '\0'
this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED);
break;
@@ -763,14 +761,12 @@ public:
case WID_NG_JOIN: // Join Game
if (this->server != nullptr) {
seprintf(_settings_client.network.last_host, lastof(_settings_client.network.last_host), "%s", this->server->address.GetHostname());
_settings_client.network.last_port = this->server->address.GetPort();
ShowNetworkLobbyWindow(this->server);
}
break;
case WID_NG_REFRESH: // Refresh
if (this->server != nullptr) NetworkUDPQueryServer(this->server->address);
if (this->server != nullptr) NetworkTCPQueryServer(this->server->address);
break;
case WID_NG_NEWGRF: // NewGRF Settings
@@ -845,7 +841,10 @@ public:
void OnQueryTextFinished(char *str) override
{
if (!StrEmpty(str)) NetworkAddServer(str);
if (!StrEmpty(str)) {
strecpy(_settings_client.network.connect_to_ip, str, lastof(_settings_client.network.connect_to_ip));
NetworkAddServer(str);
}
}
void OnResize() override
@@ -988,7 +987,7 @@ void ShowNetworkGameWindow()
first = false;
/* Add all servers from the config file to our list. */
for (const auto &iter : _network_host_list) {
NetworkAddServer(iter.c_str());
NetworkAddServer(iter);
}
}
@@ -1487,22 +1486,22 @@ struct NetworkLobbyWindow : public Window {
case WID_NL_JOIN: // Join company
/* Button can be clicked only when it is enabled. */
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company);
NetworkClientConnectGame(this->server->address, this->company);
break;
case WID_NL_NEW: // New company
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY);
NetworkClientConnectGame(this->server->address, COMPANY_NEW_COMPANY);
break;
case WID_NL_SPECTATE: // Spectate game
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR);
NetworkClientConnectGame(this->server->address, COMPANY_SPECTATOR);
break;
case WID_NL_REFRESH: // Refresh
/* Clear the information so removed companies don't remain */
for (auto &company : this->company_info) company = {};
NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
NetworkTCPQueryServer(this->server->address, true);
break;
}
}
@@ -1570,7 +1569,9 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined));
NetworkTCPQueryServer(ngl->address, true);
new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
}

View File

@@ -94,10 +94,10 @@ extern uint8 _network_reconnect;
extern CompanyMask _network_company_passworded;
void NetworkTCPQueryServer(NetworkAddress address);
void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false);
void GetBindAddresses(NetworkAddressList *addresses, uint16 port);
void NetworkAddServer(const char *b);
struct NetworkGameList *NetworkAddServer(const std::string &connection_string);
void NetworkRebuildHostList();
void UpdateNetworkGameWindow();
@@ -119,11 +119,15 @@ void NetworkExecuteLocalCommandQueue();
void NetworkFreeLocalCommandQueue();
void NetworkSyncCommandQueue(NetworkClientSocket *cs);
void NetworkError(StringID error_string);
void ShowNetworkError(StringID error_string);
void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const char *name, const char *str = "", NetworkTextMessageData data = NetworkTextMessageData());
uint NetworkCalculateLag(const NetworkClientSocket *cs);
StringID GetNetworkErrorMsg(NetworkErrorCode err);
bool NetworkFindName(char *new_name, const char *last);
const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed);
bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port);
NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port);
#endif /* NETWORK_INTERNAL_H */

View File

@@ -111,7 +111,6 @@ static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, b
/* Clear item in gamelist */
NetworkGameList *item = CallocT<NetworkGameList>(1);
address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name));
strecpy(item->info.hostname, address.GetHostname(), lastof(item->info.hostname));
item->address = address;
item->manually = manually;
NetworkGameListAddItemDelayed(item);
@@ -453,10 +452,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne
if (in_request_count > 0) flush_request();
}
if (item->info.hostname[0] == '\0') {
seprintf(item->info.hostname, lastof(item->info.hostname), "%s", client_addr->GetHostname());
}
if (client_addr->GetAddress()->ss_family == AF_INET6) {
strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name));
}