From 65c5a647191a9a10a532bb9d67da6938f5eace1b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 10:50:28 +0200 Subject: [PATCH 01/12] Fix: [Network] errno and strerror do not handle network errors on Windows --- src/network/core/address.cpp | 14 +++++++------- src/network/core/core.cpp | 17 +++++++++++++++++ src/network/core/os_abstraction.h | 25 ++++++++++++++++++++++--- src/network/core/tcp.cpp | 6 +++--- src/network/core/tcp_http.cpp | 2 +- src/network/core/tcp_listen.h | 4 ++-- src/network/core/udp.cpp | 4 ++-- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 8c69094385..fc19439e00 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -316,7 +316,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) 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, strerror(errno)); + DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkGetLastErrorString()); return INVALID_SOCKET; } @@ -331,7 +331,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) if (err != 0) #endif { - DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno)); + DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } @@ -369,7 +369,7 @@ static SOCKET ListenLoopProc(addrinfo *runp) 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, strerror(errno)); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkGetLastErrorString()); return INVALID_SOCKET; } @@ -380,24 +380,24 @@ static SOCKET ListenLoopProc(addrinfo *runp) 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, strerror(errno)); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkGetLastErrorString()); } #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, strerror(errno)); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkGetLastErrorString()); } #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, strerror(errno)); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkGetLastErrorString()); 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, strerror(errno)); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 0aeb9c65ce..8c5c5c2292 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -13,6 +13,7 @@ #include "../../debug.h" #include "os_abstraction.h" #include "packet.h" +#include "../../string_func.h" #include "../../safeguards.h" @@ -48,6 +49,22 @@ void NetworkCoreShutdown() #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) */ /** * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 836cfeae8f..a015c4374f 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -23,9 +23,17 @@ #include #include -#define GET_LAST_ERROR() WSAGetLastError() +/** + * 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 + +const char *NetworkGetErrorString(int error); + /* Windows has some different names for some types */ typedef unsigned long in_addr_t; @@ -51,7 +59,8 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define GET_LAST_ERROR() (errno) +# define NetworkGetLastError() (errno) +# define NetworkGetErrorString(error) (strerror(error)) /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -101,7 +110,8 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define GET_LAST_ERROR() (sock_errno()) +# define NetworkGetLastError() (sock_errno()) +# define NetworkGetErrorString(error) (strerror(error)) /* Includes needed for OS/2 systems */ # include @@ -173,6 +183,15 @@ 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. diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 72e66a0b55..b9ba33f00e 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -86,7 +86,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) while ((p = this->packet_queue) != nullptr) { res = p->TransferOut(send, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong.. close client! */ if (!closing_down) { @@ -136,7 +136,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); @@ -164,7 +164,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index d88ea711d3..04bc6a03a4 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -228,7 +228,7 @@ 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 = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 53a3d57cc9..c11727ba71 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); + DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); + DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index e8299f7b62..abbb1bbdb5 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -94,7 +94,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: %i", GET_LAST_ERROR()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %i", NetworkGetLastError()); } } @@ -103,7 +103,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), GET_LAST_ERROR()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), NetworkGetLastError()); if (!all) break; } From cf8c1aa860e05e696f9829e3b031a22ca82781ef Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 12:13:06 +0200 Subject: [PATCH 02/12] Change: [Network] Use string error messages instead of numeric error numbers that need to be looked up --- src/network/core/os_abstraction.h | 2 ++ src/network/core/tcp.cpp | 10 +++++----- src/network/core/tcp_http.cpp | 4 ++-- src/network/core/tcp_listen.h | 4 ++-- src/network/core/udp.cpp | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index a015c4374f..7af3fd163e 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -31,6 +31,8 @@ #define NetworkGetLastError() WSAGetLastError() #undef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET const char *NetworkGetErrorString(int error); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index b9ba33f00e..f23b202c8b 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -90,7 +90,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) if (err != EWOULDBLOCK) { /* Something went wrong.. close client! */ if (!closing_down) { - DEBUG(net, 0, "send failed with error %d", err); + DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); } return SPS_CLOSED; @@ -138,8 +138,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); return nullptr; } @@ -166,8 +166,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); return nullptr; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 04bc6a03a4..d57f4eceb7 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -230,8 +230,8 @@ int NetworkHTTPSocketHandler::Receive() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); return -1; } /* Connection would block, so stop for now */ diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index c11727ba71..168f49f947 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); + DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); + DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index abbb1bbdb5..df5140e2b5 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -94,7 +94,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: %i", NetworkGetLastError()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkGetLastErrorString()); } } @@ -103,7 +103,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), NetworkGetLastError()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkGetLastErrorString()); if (!all) break; } From cbad518bf3c7e6d05b62d8c1d063b89d974e5f33 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 10:56:26 +0200 Subject: [PATCH 03/12] Codechange: [Network] Do not leak os_abstraction.h via network_func --- src/console_cmds.cpp | 4 ++-- src/network/network.cpp | 15 ++++++++------- src/network/network_func.h | 5 ++--- src/network/network_gui.cpp | 6 +++--- src/openttd.cpp | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index f8d9eb1b0c..048b8b3e93 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -900,7 +900,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s:%d...", _settings_client.network.last_host, _settings_client.network.last_port); - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), playas); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, playas); return true; } @@ -942,7 +942,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) IConsolePrintF(CC_DEFAULT, " port: %s", port); } - NetworkClientConnectGame(NetworkAddress(ip, rport), join_as); + NetworkClientConnectGame(ip, rport, join_as); return true; } diff --git a/src/network/network.cpp b/src/network/network.cpp index 1e0838684e..812456b4e2 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -688,16 +688,16 @@ public: /* Used by clients, to connect to a server */ -void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password, const char *join_company_password) +void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password) { if (!_network_available) return; - if (address.GetPort() == 0) return; + if (port == 0) return; if (!NetworkValidateClientName()) return; - strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host)); - _settings_client.network.last_port = address.GetPort(); + 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; @@ -708,7 +708,7 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(address); + new TCPClientConnecter(NetworkAddress(hostname, port)); } static void NetworkInitGameInfo() @@ -1059,12 +1059,13 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(NetworkAddress address) +void NetworkStartDebugLog(const char *hostname, uint16 port) { extern SOCKET _debug_socket; // Comes from debug.c - DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort()); + DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port); + NetworkAddress address(hostname, port); SOCKET s = address.Connect(); if (s == INVALID_SOCKET) { DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); diff --git a/src/network/network_func.h b/src/network/network_func.h index c1271f69c6..66719b2166 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -17,7 +17,6 @@ // #define DEBUG_DUMP_COMMANDS // #define DEBUG_FAILED_DUMP_COMMANDS -#include "core/address.h" #include "network_type.h" #include "../console_type.h" #include "../gfx_type.h" @@ -48,12 +47,12 @@ 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(NetworkAddress address); +void NetworkStartDebugLog(const char *hostname, uint16 port); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 68208ee3fc..ebba47d851 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1470,15 +1470,15 @@ struct NetworkLobbyWindow : public Window { case WID_NL_JOIN: // Join company /* Button can be clicked only when it is enabled. */ - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), this->company); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company); break; case WID_NL_NEW: // New company - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_NEW_COMPANY); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY); break; case WID_NL_SPECTATE: // Spectate game - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); break; case WID_NL_REFRESH: // Refresh diff --git a/src/openttd.cpp b/src/openttd.cpp index b204ca5a2f..016d482873 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -495,7 +495,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(NetworkAddress(network_conn, rport), join_as, join_server_password, join_company_password); + NetworkClientConnectGame(network_conn, rport, join_as, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -779,7 +779,7 @@ int openttd_main(int argc, char *argv[]) ParseConnectionString(&port, debuglog_conn); if (port != nullptr) rport = atoi(port); - NetworkStartDebugLog(NetworkAddress(debuglog_conn, rport)); + NetworkStartDebugLog(debuglog_conn, rport); } if (!HandleBootstrap()) { @@ -1491,7 +1491,7 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); From 84985c1223757766bad17b917a5579f84e1dec64 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:17:03 +0200 Subject: [PATCH 04/12] Codechange: [Network] Do not leak os_abstraction.h via fios.h --- src/fios.h | 2 +- src/network/core/CMakeLists.txt | 1 + src/network/core/tcp_content.h | 76 +----------------------- src/network/core/tcp_content_type.h | 90 +++++++++++++++++++++++++++++ src/newgrf_config.cpp | 1 + 5 files changed, 94 insertions(+), 76 deletions(-) create mode 100644 src/network/core/tcp_content_type.h diff --git a/src/fios.h b/src/fios.h index 8d8faac07b..3a16b6426a 100644 --- a/src/fios.h +++ b/src/fios.h @@ -13,7 +13,7 @@ #include "gfx_type.h" #include "company_base.h" #include "newgrf_config.h" -#include "network/core/tcp_content.h" +#include "network/core/tcp_content_type.h" /** Special values for save-load window for the data parameter of #InvalidateWindowData. */ diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 777d15d841..c9368a5b46 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -17,6 +17,7 @@ add_files( tcp_connect.cpp tcp_content.cpp tcp_content.h + tcp_content_type.h tcp_game.cpp tcp_game.h tcp_http.cpp diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index ef8ae3a10a..f927021f4d 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -16,81 +16,7 @@ #include "tcp.h" #include "packet.h" #include "../../debug.h" - -/** The values in the enum are important; they are used as database 'keys' */ -enum ContentType { - CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types - CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics - CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF - CONTENT_TYPE_AI = 3, ///< The content consists of an AI - CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library - CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario - CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap - CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds - CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music - CONTENT_TYPE_GAME = 9, ///< The content consists of a game script - CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library - CONTENT_TYPE_END, ///< Helper to mark the end of the types -}; - -/** Enum with all types of TCP content packets. The order MUST not be changed **/ -enum PacketContentType { - PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type - PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs - PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs - PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5 - PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content - PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID - PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID - PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period) -}; - -/** Unique identifier for the content. */ -enum ContentID { - INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content. -}; - -/** Container for all important information about a piece of content. */ -struct ContentInfo { - /** The state the content can be in. */ - enum State { - UNSELECTED, ///< The content has not been selected - SELECTED, ///< The content has been manually selected - AUTOSELECTED, ///< The content has been selected as dependency - ALREADY_HERE, ///< The content is already at the client side - DOES_NOT_EXIST, ///< The content does not exist in the content system - INVALID, ///< The content's invalid - }; - - ContentType type; ///< Type of content - ContentID id; ///< Unique (server side) ID for the content - uint32 filesize; ///< Size of the file - char filename[48]; ///< Filename (for the .tar.gz; only valid on download) - char name[32]; ///< Name of the content - char version[16]; ///< Version of the content - char url[96]; ///< URL related to the content - char description[512]; ///< Description of the content - uint32 unique_id; ///< Unique ID; either GRF ID or shortname - byte md5sum[16]; ///< The MD5 checksum - uint8 dependency_count; ///< Number of dependencies - ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids) - uint8 tag_count; ///< Number of tags - char (*tags)[32]; ///< Malloced array of tags (strings) - State state; ///< Whether the content info is selected (for download) - bool upgrade; ///< This item is an upgrade - - ContentInfo(); - ~ContentInfo(); - - void TransferFrom(ContentInfo *other); - - size_t Size() const; - bool IsSelected() const; - bool IsValid() const; -#ifndef OPENTTD_MSU - const char *GetTextfile(TextfileType type) const; -#endif /* OPENTTD_MSU */ -}; +#include "tcp_content_type.h" /** Base socket handler for all Content TCP sockets */ class NetworkContentSocketHandler : public NetworkTCPSocketHandler { diff --git a/src/network/core/tcp_content_type.h b/src/network/core/tcp_content_type.h new file mode 100644 index 0000000000..f4dbc0c6ee --- /dev/null +++ b/src/network/core/tcp_content_type.h @@ -0,0 +1,90 @@ +/* + * 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 . + */ + +/** + * @file tcp_content_type.h Basic types related to the content on the content server. + */ + +#ifndef NETWORK_CORE_TCP_CONTENT_TYPE_H +#define NETWORK_CORE_TCP_CONTENT_TYPE_H + +/** The values in the enum are important; they are used as database 'keys' */ +enum ContentType { + CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types + CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics + CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF + CONTENT_TYPE_AI = 3, ///< The content consists of an AI + CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library + CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario + CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap + CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds + CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music + CONTENT_TYPE_GAME = 9, ///< The content consists of a game script + CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library + CONTENT_TYPE_END, ///< Helper to mark the end of the types +}; + +/** Enum with all types of TCP content packets. The order MUST not be changed **/ +enum PacketContentType { + PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type + PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs + PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs + PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5 + PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content + PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID + PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID + PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period) +}; + +/** Unique identifier for the content. */ +enum ContentID { + INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content. +}; + +/** Container for all important information about a piece of content. */ +struct ContentInfo { + /** The state the content can be in. */ + enum State { + UNSELECTED, ///< The content has not been selected + SELECTED, ///< The content has been manually selected + AUTOSELECTED, ///< The content has been selected as dependency + ALREADY_HERE, ///< The content is already at the client side + DOES_NOT_EXIST, ///< The content does not exist in the content system + INVALID, ///< The content's invalid + }; + + ContentType type; ///< Type of content + ContentID id; ///< Unique (server side) ID for the content + uint32 filesize; ///< Size of the file + char filename[48]; ///< Filename (for the .tar.gz; only valid on download) + char name[32]; ///< Name of the content + char version[16]; ///< Version of the content + char url[96]; ///< URL related to the content + char description[512]; ///< Description of the content + uint32 unique_id; ///< Unique ID; either GRF ID or shortname + byte md5sum[16]; ///< The MD5 checksum + uint8 dependency_count; ///< Number of dependencies + ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids) + uint8 tag_count; ///< Number of tags + char (*tags)[32]; ///< Malloced array of tags (strings) + State state; ///< Whether the content info is selected (for download) + bool upgrade; ///< This item is an upgrade + + ContentInfo(); + ~ContentInfo(); + + void TransferFrom(ContentInfo *other); + + size_t Size() const; + bool IsSelected() const; + bool IsValid() const; +#ifndef OPENTTD_MSU + const char *GetTextfile(TextfileType type) const; +#endif /* OPENTTD_MSU */ +}; + +#endif /* NETWORK_CORE_TCP_CONTENT_TYPE_H */ diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index a0e60ef755..d7919e32ae 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -17,6 +17,7 @@ #include "window_func.h" #include "progress.h" #include "video/video_driver.hpp" +#include "string_func.h" #include "strings_func.h" #include "textfile_gui.h" #include "thread.h" From 8c2e3a004ee362b59bc9f918aa4fb6d4d7baedba Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:20:27 +0200 Subject: [PATCH 05/12] Codechange: [Network] Do not leak os_abstraction.h via base_media_func.h --- src/base_media_func.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base_media_func.h b/src/base_media_func.h index 8491f0e510..f5a5995f17 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -274,7 +274,7 @@ template return p; } -#include "network/network_content.h" +#include "network/core/tcp_content_type.h" template const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s) { From 4880ec29e4649655e775c36cd6bd8c0927d13ee1 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:30:56 +0200 Subject: [PATCH 06/12] Change: [Network] Safeguard from using errno/strerror for handling network errors They are likely not working as expected on Windows, so prevent their usage. Winsock does not set errno and strerror does not return anything useful for Winsock error numbers. --- src/safeguards.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/safeguards.h b/src/safeguards.h index 5351116ecb..e3d6c4a3e4 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -69,4 +69,17 @@ #undef abs #endif +#if defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) +/* Use NetworkGetLastError() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkGetLastError abstracts that away. */ +#ifdef errno +#undef errno +#endif +#define errno SAFEGUARD_DO_NOT_USE_THIS_METHOD + +/* Use NetworkGetLastErrorString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkGetLastErrorString abstracts that away. */ +#define strerror SAFEGUARD_DO_NOT_USE_THIS_METHOD +#endif /* defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) */ + #endif /* SAFEGUARDS_H */ From b54d8a49fb8d635545b2251bfdf41ebee60edc0c Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:14:44 +0200 Subject: [PATCH 07/12] Feature: allow non-ASCII currency separators --- src/settings.cpp | 3 --- src/settings_gui.cpp | 6 +++--- src/table/currency_settings.ini | 4 ++-- src/table/settings.h.preamble | 3 --- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index fdb26368cf..4a04784bca 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -563,8 +563,6 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp *(char**)ptr = p == nullptr ? nullptr : stredup((const char*)p); break; - case SLE_VAR_CHAR: if (p != nullptr) *(char *)ptr = *(const char *)p; break; - default: NOT_REACHED(); } break; @@ -716,7 +714,6 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp } break; - case SLE_VAR_CHAR: buf[0] = *(char*)ptr; buf[1] = '\0'; break; default: NOT_REACHED(); } break; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f5872c648f..52d900e6e8 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2676,7 +2676,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_SEPARATOR: SetDParamStr(0, _custom_currency.separator); str = STR_JUST_RAW_STRING; - len = 1; + len = sizeof(_custom_currency.separator) - 1; // Number of characters excluding '\0' termination line = WID_CC_SEPARATOR; break; @@ -2684,7 +2684,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_PREFIX: SetDParamStr(0, _custom_currency.prefix); str = STR_JUST_RAW_STRING; - len = 12; + len = sizeof(_custom_currency.prefix) - 1; // Number of characters excluding '\0' termination line = WID_CC_PREFIX; break; @@ -2692,7 +2692,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_SUFFIX: SetDParamStr(0, _custom_currency.suffix); str = STR_JUST_RAW_STRING; - len = 12; + len = sizeof(_custom_currency.suffix) - 1; // Number of characters excluding '\0' termination line = WID_CC_SUFFIX; break; diff --git a/src/table/currency_settings.ini b/src/table/currency_settings.ini index c242c83a87..3e51d0240a 100644 --- a/src/table/currency_settings.ini +++ b/src/table/currency_settings.ini @@ -10,7 +10,6 @@ static const SettingDesc _currency_settings[] = { }; [templates] SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), -SDT_CHR = SDT_CHR($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), SDT_END = SDT_END() @@ -42,9 +41,10 @@ def = 1 min = 0 max = UINT16_MAX -[SDT_CHR] +[SDT_STR] base = CurrencySpec var = separator +type = SLE_STRBQ def = ""."" cat = SC_BASIC diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index d7084d7247..c3e0678b7b 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -107,9 +107,6 @@ static size_t ConvertLandscape(const char *value); #define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extra, startup)\ SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extra, startup) -#define SDT_CHR(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extra, startup)\ - SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extra, startup) - #define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, load, cat, extra, startup)\ SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, load, from, to, cat, extra, startup) From 0e449f20dcec4e9a04b218205453b40deb947382 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:24:33 +0200 Subject: [PATCH 08/12] Codechange: writing and string validation to its own functions --- src/settings.cpp | 91 ++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 4a04784bca..3095c6079c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -495,6 +495,54 @@ static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) WriteValue(ptr, sd->save.conv, (int64)val); } +/** + * Set the string value of a setting. + * @param ptr Pointer to the storage location (might be a pointer to a pointer). + * @param sld Pointer to the information for the conversions and limitations to apply. + * @param p The string to save. + */ +static void Write_ValidateString(void *ptr, const SaveLoad *sld, const char *p) +{ + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); + break; + + case SLE_VAR_STR: + case SLE_VAR_STRQ: + free(*(char**)ptr); + *(char**)ptr = p == nullptr ? nullptr : stredup(p); + break; + + default: NOT_REACHED(); + } +} + +/** + * Set the string value of a setting. + * @param ptr Pointer to the std::string. + * @param sld Pointer to the information for the conversions and limitations to apply. + * @param p The string to save. + */ +static void Write_ValidateStdString(void *ptr, const SaveLoad *sld, const char *p) +{ + std::string *dst = reinterpret_cast(ptr); + + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STR: + case SLE_VAR_STRQ: + if (p != nullptr) { + dst->assign(p); + } else { + dst->clear(); + } + break; + + default: NOT_REACHED(); + } +} + /** * Load values from a group of an IniFile structure into the internal representation * @param ini pointer to IniFile structure that holds administrative information @@ -551,36 +599,11 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp break; case SDT_STRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); - break; - - case SLE_VAR_STR: - case SLE_VAR_STRQ: - free(*(char**)ptr); - *(char**)ptr = p == nullptr ? nullptr : stredup((const char*)p); - break; - - default: NOT_REACHED(); - } + Write_ValidateString(ptr, sld, (const char *)p); break; case SDT_STDSTRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STR: - case SLE_VAR_STRQ: - if (p != nullptr) { - reinterpret_cast(ptr)->assign((const char *)p); - } else { - reinterpret_cast(ptr)->clear(); - } - break; - - default: NOT_REACHED(); - } - + Write_ValidateStdString(ptr, sld, (const char *)p); break; case SDT_INTLIST: { @@ -2082,13 +2105,13 @@ bool SetSettingValue(uint index, const char *value, bool force_newgame) const SettingDesc *sd = &_settings[index]; assert(sd->save.conv & SLF_NO_NETWORK_SYNC); - if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) { - char **var = (char**)GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); - free(*var); - *var = strcmp(value, "(null)") == 0 ? nullptr : stredup(value); - } else { - char *var = (char*)GetVariableAddress(nullptr, &sd->save); - strecpy(var, value, &var[sd->save.length - 1]); + if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ && strcmp(value, "(null)") == 0) { + value = nullptr; + } + + void *ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); + if (sd->desc.cmd == SDT_STRING) { + Write_ValidateString(ptr, &sd->save, value); } if (sd->desc.proc != nullptr) sd->desc.proc(0); From 31c87ba90813a38222002d717d7f03dcf30ebced Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:25:53 +0200 Subject: [PATCH 09/12] Fix: truncating strings in settings could leave invalid Utf8 characters --- src/settings.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index 3095c6079c..9b97107a34 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -506,7 +506,12 @@ static void Write_ValidateString(void *ptr, const SaveLoad *sld, const char *p) switch (GetVarMemType(sld->conv)) { case SLE_VAR_STRB: case SLE_VAR_STRBQ: - if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); + if (p != nullptr) { + char *begin = (char*)ptr; + char *end = begin + sld->length - 1; + strecpy(begin, p, end); + str_validate(begin, end, SVS_NONE); + } break; case SLE_VAR_STR: From b89dba7e4e60c3387f74690687174080bac911cb Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 27 Apr 2021 17:53:43 +0000 Subject: [PATCH 10/12] Update: Translations from eints japanese: 26 changes by scabtert catalan: 43 changes by J0anJosep --- src/lang/catalan.txt | 49 +++++++++++++++++++++++++++++++++++++++---- src/lang/japanese.txt | 26 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 5ad9f92378..6dd49f586b 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit (MYR) STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducció per l'esquerra STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducció per la dreta -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Estil dels noms de poblacions +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Estil dels noms de les poblacions: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selecciona l'estil dels noms de poblacions ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 mesos STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona l'idioma de la interfície +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marqueu la casella per mostrar l'OpenTTD a pantalla completa. @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Arracades: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Canvia la corbata o les arracades +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privada +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Pública # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}El nom d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Posa una contrasenya STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protegeix la teva partida amb una contrasenya si no vols que sigui accessible a desconeguts +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilitat +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Permet establir si altres persones poden veure el vostre servidor a la llista pública. STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Màxim nombre de clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Tria el nombre màxim de clients. No és necessari omplir tots els llocs. @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Companyia protegida: escriviu-ne la contrasenya # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Llista de clients +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadors en línia # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nom del servidor on esteu jugant +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editeu el nom del vostre servidor. +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nom del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilitat +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Permet establir si altres persones poden veure el vostre servidor a la llista pública. +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}El vostre nom de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Editeu el vostre nom de jugador. +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :El vostre nom de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Accions d'administració que s'han de realitzar per a aquest client. +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Accions d'administració que s'han de realitzar per a aquesta companyia. +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Uniu-vos a aquesta companyia. +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envia un missatge a aquest jugador. +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envia un missatge a tots els jugadors de la companyia. +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envieu un missatge a tots els espectadors. +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadors +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Companyia nova) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una companyia nova i uniu-vos. +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Aquest ets tu. +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Aquest és l'hoste de la partida. + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Treu +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Expulsa +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Esborra +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Desbloca la contrasenya + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acció de l'administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Esteu segur que voleu treure el jugador «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Esteu segur que voleu expulsar el jugador «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Esteu segur que voleu esborrar la companyia «{COMPANY}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Esteu segur que voleu restablir la contrasenya de la companyia «{COMPANY}»? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Client @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}No s'ha STR_NETWORK_ERROR_CLIENT_START :{WHITE}No s'ha pogut connectar STR_NETWORK_ERROR_TIMEOUT :{WHITE}La connexió #{NUM} ha esgotat el temps d'espera STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}S'ha obtingut un error de protocol i s'ha tancat la connexió +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}No s'ha escollit un nom per al vostre jugador. El nom es pot establir a la part superior de la finestra de mode multijugador. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}La revisió d'aquest client no concorda amb la revisió del servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Contrasenya incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}El servidor està ple @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Has tard STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}El teu ordinador és massa lent per mantenir-se connectat al servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}El teu ordinador ha tardat massa a descarregar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}El teu ordinador ha tardat massa a unir-se al servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}El vostre nom de jugador no és vàlid. ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :no s'ha rebut l STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :temps d'espera general esgotat STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :la descàrrega del mapa ha tardat massa STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :el processat del mapa ha tardat massa +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nom de client no vàlid ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible pèrdua de connexió diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 867f929d1f..b066e74c37 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -1114,6 +1114,7 @@ STR_TERRAIN_TYPE_HILLY :丘陵地 STR_TERRAIN_TYPE_MOUNTAINOUS :山岳地 STR_TERRAIN_TYPE_ALPINIST :山脈地帯 STR_TERRAIN_TYPE_CUSTOM :カスタム高度 +STR_TERRAIN_TYPE_CUSTOM_VALUE :カスタム高度 ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :寛大 STR_CITY_APPROVAL_TOLERANT :寛容 @@ -1197,6 +1198,7 @@ STR_CONFIG_SETTING_CITY_APPROVAL :地域の再編 STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :会社が街域で引き起こした騒音(主に空港)や環境破壊がどの程度、街での評価や同じ地域での更なる建設行為に影響するかを設定します STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :マップ高さ限界: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(自動) STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}マップの最高高さをこの値には設定出来ません。少なくとも1箇所以上この値より高い山があります。 STR_CONFIG_SETTING_AUTOSLOPE :建物/路線の自動地形追従: {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :撤去を行わないで建物や路線がある土地の地形を変更することを可能にします。建物/路線は変更された地形に自動で追従します。 @@ -1340,6 +1342,7 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :石油精製所 STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :石油精製所はマップの外周付近にのみ建設されます。つまり、外周が海のマップでは海岸沿いに建設されるということです STR_CONFIG_SETTING_SNOWLINE_HEIGHT :雪線の位置: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :亜寒帯気候での雪線の高さを設定します。雪は産業と街の成長に影響があります +STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :亜寒帯の風景のおおよその雪の量を制御します。雪はまた、産業の生成と町の成長要件にも影響を及ぼします。マップの生成中にのみ使用されます。海抜のすぐ上の土地は常に雪がありません STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :地形の起伏: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesisのみ) 地形の起伏度を設定します。なだらかな地形では丘陵の数は減り、裾野が長くなります。起伏が多い地形では丘陵が多くなりますが、似たり寄ったりな地形の繰り返しに見えることがあります @@ -1450,6 +1453,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :建設ツール STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :橋やトンネルなどを建設した後もツールバーを開いたままにします STR_CONFIG_SETTING_EXPENSES_LAYOUT :財政ウィンドウのグループ分け: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :財政ウィンドウのレイアウトを収入部門・支出部門でグループ分けするかどうかを設定します +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :早送りが有効になっている場合のゲームの進行速度を制限します。0 =制限なし(コンピューターが許す限り高速)。100%未満の値は、ゲームの速度を低下させます。上限はコンピュータの仕様によって異なり、ゲームによって異なる場合があります。 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}%通常のゲーム速度 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :制限なし(コンピューターが許す限り高速) @@ -1500,6 +1504,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :マルチプレ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :マルチプレイヤーゲームでもAIのライバル企業が登場するかを設定します STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :命令コード処理上限: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :AIやゲームスクリプトが一つの「詰め込み指令」を処理する際に、一度に演算できる命令コード数を設定します。一般に値を小さくした場合、ゲームへの負荷が軽減されます +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :スクリプトあたりの最大メモリ使用量:{STRING} STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB STR_CONFIG_SETTING_SERVINT_ISPERCENT :最大信頼度を点検要件化: {STRING} @@ -1631,6 +1636,7 @@ STR_CONFIG_SETTING_ZOOM_MIN :最大ズーム STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :ズームインの最大倍率を設定します。倍率を高くすればするほどメモリー使用量が増えます STR_CONFIG_SETTING_ZOOM_MAX :最大ズームアウトレベル:{STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :ズームアウトの最大倍率を設定します。ズームアウトの倍率が大きいと、処理遅延が発生する可能性があります +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :使用する最高解像度のスプライト:{STRING} STR_CONFIG_SETTING_SPRITE_ZOOM_MIN_HELPTEXT :スプライトに使用する最大解像度を制限します。 スプライトの解像度を制限すると、使用可能な場合でも高解像度のグラフィックを使用できなくなります。 これにより、高解像度のグラフィックを使用する場合と使用しない場合のGRFファイルを組み合わせて使用する場合に、ゲームの外観を統一することができます。 STR_CONFIG_SETTING_ZOOM_LVL_MIN :4倍 STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2倍 @@ -2002,6 +2008,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}ゲー STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}パスワードを設定 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}ゲームをパスワードで保護することができます。一般から公然とアクセスされたくない場合等に設定します +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}可視性 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}接続者数: {NUM} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}最大接続数: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}接続できるクライアントの最大数を指定します。必ずしも全スロットを埋める必要はありません @@ -2068,8 +2075,13 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}この STR_NETWORK_COMPANY_LIST_CLIENT_LIST :クライアントリスト # Network client list +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}他の人があなたのサーバーを公開リストで見ることができるかどうか +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}すべての観客にメッセージを送る +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}ゲームのホストです +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}プレーヤー「{STRING}」を追放してもよろしいですか? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}会社 '{COMPANY}'のパスワードをリセットしてもよろしいですか? STR_NETWORK_SERVER :サーバー STR_NETWORK_CLIENT :クライアント @@ -2119,6 +2131,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}不正 STR_NETWORK_ERROR_SERVER_FULL :{WHITE}サーバが満員です STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}サーバー側であなたの参加が禁止されています STR_NETWORK_ERROR_KICKED :{WHITE}ゲームから追放されました +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}理由:{STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}このサーバーではチート行為は許可されていません STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}サーバーに送ったコマンド数が過剰です STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}パスワード入力時間切れです @@ -2256,6 +2269,7 @@ STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}いい STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}ダウンロードに失敗しました STR_MISSING_GRAPHICS_ERROR :{BLACK}グラフィックのダウンロードに失敗しました。{}手動でダウンロードしてください。 +STR_MISSING_GRAPHICS_ERROR_QUIT :{BLACK}OpenTTDをやめる # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}透過表示設定 @@ -2404,6 +2418,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}道路 STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}軌道の建設/撤去を切り替えます STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}道路の種類を変更/アップグレードします.Shiftは、コスト見積もりの作成/表示を切り替えます +STR_ROAD_NAME_ROAD :道路 STR_ROAD_NAME_TRAM :トラムウェイ # Road depot construction window @@ -2492,6 +2507,7 @@ STR_TREES_RANDOM_TYPE :{BLACK}ラン STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}ランダムな樹類で植林します。Shift+クリックで費用を見積もります STR_TREES_RANDOM_TREES_BUTTON :{BLACK}ランダムに広域植林 STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}地表全体にランダムに植林します +STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}風景の上をドラッグして、単一の木を植えます。 STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}グローブ STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}風景をドラッグして小さな森を植えます STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}風景の上をドラッグして、大きな森を植えます。 @@ -2710,6 +2726,7 @@ STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMA STR_FRAMERATE_FPS_WARN :{YELLOW} {DECIMAL}フレーム/秒 STR_FRAMERATE_FPS_BAD :{RED} {DECIMAL}FPS STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} +STR_FRAMERATE_BYTES_WARN :{YELLOW} {BYTES} ############ Leave those lines in this order!! STR_FRAMERATE_GAMELOOP :{BLACK}ゲームループの合計: STR_FRAMERATE_GL_ECONOMY :{BLACK}貨物の取り扱い: @@ -2724,6 +2741,8 @@ STR_FRAMERATE_SOUND :{BLACK}サウ STR_FRAMETIME_CAPTION_GAMELOOP :ゲームループ STR_FRAMETIME_CAPTION_GL_ECONOMY :貨物の取り扱い STR_FRAMETIME_CAPTION_GL_TRAINS :切符 +STR_FRAMETIME_CAPTION_GL_SHIPS :船のティック +STR_FRAMETIME_CAPTION_GL_AIRCRAFT :航空機ティック STR_FRAMETIME_CAPTION_GL_LANDSCAPE :ワールドティック STR_FRAMETIME_CAPTION_GL_LINKGRAPH :リンクグラフの遅延 STR_FRAMETIME_CAPTION_DRAWING :グラフィックレンダリング @@ -2804,6 +2823,7 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}ハイ STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}サイズ: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} × {NUM} +STR_MAPGEN_TERRAIN_TYPE_QUERY_CAPT :{WHITE}ターゲットの最高の高さ STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}積雪量(%) STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}砂漠領域(%) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}開始年の変更 @@ -2880,6 +2900,7 @@ STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: STR_NEWGRF_SETTINGS_PALETTE :{BLACK}パレット: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE_DEFAULT :デフォルト(D) STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP :デフォルト(D)/ 32 bpp +STR_NEWGRF_SETTINGS_PALETTE_LEGACY :レガシー(W) STR_NEWGRF_SETTINGS_PALETTE_LEGACY_32BPP :レガシー(W)/ 32 bpp STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}設定: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PARAMETER_NONE :なし @@ -3334,6 +3355,7 @@ STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_L STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}産業の名前です - 名前をクリックするとこの産業拠点の場所にメイン画面を移動します。Ctrl+クリックでこの産業拠点の場所を新たなビューポートに表示します STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}受け取った貨物: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER :{BLACK}生産された貨物:{SILVER} {STRING} STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :すべての貨物タイプ STR_INDUSTRY_DIRECTORY_FILTER_NONE :なし @@ -3653,6 +3675,7 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}機関 STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}メイン画面を列車に中心します。ダブルクリックで列車をメイン画面で追従します。Ctrl+クリックで列車の場所で新しいビューポートでを開きます。 +STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}航空機の位置に関する中央のメインビュー。ダブルクリックすると、メインビューで航空機が表示されます。Ctrl +クリックすると、航空機の位置に新しいビューポートが開きます STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}列車を列車庫へ回送します。Ctrl+クリックすると点検後、再出庫します STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}車両を車庫へ回送します。Ctrl+クリックすると点検後、再出庫します @@ -4094,9 +4117,11 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}選択 STR_AI_LIST_CANCEL :{BLACK}キャンセル STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}スクリプトを変更しません +STR_SCREENSHOT_CAPTION :{WHITE}スクリーンショットを撮る STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}スクリーンショットを完全に拡大 STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}地図全体のスクリーンショット STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}ハイトマップスクリーンショット +STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}ミニマップのスクリーンショット # AI Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} パラメータ @@ -4237,6 +4262,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}全額 STR_ERROR_CURRENCY_REQUIRED :{WHITE}{CURRENCY_LONG}が必要です STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}借入金を返済できません STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}借入金を送金することはできません +STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}この会社にお金を渡すことはできません... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}会社を買収できません STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}会社の本社ビルを建設できません STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}この会社の株を25%購入できません From 015e3b412ebe709e1596179e86fde364cf19f52a Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 19:32:51 +0200 Subject: [PATCH 11/12] Cleanup: remove #ifdefs for compiling the old content server --- src/network/core/tcp_content.cpp | 6 ------ src/network/core/tcp_content.h | 2 -- src/network/core/tcp_content_type.h | 2 -- 3 files changed, 10 deletions(-) diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 55319e430d..488be50003 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -10,14 +10,12 @@ */ #include "../../stdafx.h" -#ifndef OPENTTD_MSU #include "../../textfile_gui.h" #include "../../newgrf_config.h" #include "../../base_media_base.h" #include "../../ai/ai.hpp" #include "../../game/game.hpp" #include "../../fios.h" -#endif /* OPENTTD_MSU */ #include "tcp_content.h" #include "../../safeguards.h" @@ -92,7 +90,6 @@ bool ContentInfo::IsValid() const return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END; } -#ifndef OPENTTD_MSU /** * Search a textfile file next to this file in the content list. * @param type The type of the textfile to search for. @@ -139,7 +136,6 @@ const char *ContentInfo::GetTextfile(TextfileType type) const if (tmp == nullptr) return nullptr; return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp); } -#endif /* OPENTTD_MSU */ void NetworkContentSocketHandler::Close() { @@ -236,7 +232,6 @@ bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this-> bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); } bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); } -#ifndef OPENTTD_MSU /** * Helper to get the subdirectory a #ContentInfo is located in. * @param type The type of content. @@ -261,4 +256,3 @@ Subdirectory GetContentInfoSubDir(ContentType type) case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR; } } -#endif /* OPENTTD_MSU */ diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index f927021f4d..52cae1e0ed 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -129,8 +129,6 @@ public: bool ReceivePackets(); }; -#ifndef OPENTTD_MSU Subdirectory GetContentInfoSubDir(ContentType type); -#endif /* OPENTTD_MSU */ #endif /* NETWORK_CORE_TCP_CONTENT_H */ diff --git a/src/network/core/tcp_content_type.h b/src/network/core/tcp_content_type.h index f4dbc0c6ee..4dc20f46bb 100644 --- a/src/network/core/tcp_content_type.h +++ b/src/network/core/tcp_content_type.h @@ -82,9 +82,7 @@ struct ContentInfo { size_t Size() const; bool IsSelected() const; bool IsValid() const; -#ifndef OPENTTD_MSU const char *GetTextfile(TextfileType type) const; -#endif /* OPENTTD_MSU */ }; #endif /* NETWORK_CORE_TCP_CONTENT_TYPE_H */ From 8fa53f543a5929bdbb12c8776ae9577594f9eba7 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 27 Apr 2021 20:18:43 +0200 Subject: [PATCH 12/12] Change: [Network] lower TCP connect() timeout to 3s (#9112) Currently we use default OS timeout for TCP connections, which is around 30s. 99% of the users will never notice this, but there are a few cases where this is an issue: - If you have a broken IPv6 connection, using Content Service is first tried over IPv6. Only after 30s it times out and tries IPv4. Nobody is waiting for that 30s. - Upcoming STUN support has several methods of establishing a connection between client and server. This requires feedback from connect() to know if any method worked (they have to be tried one by one). With 30s, this would take a very long time. What is good to mention, is that there is no good value here. Any value will have edge-cases where the experience is suboptimal. But with 3s we support most of the stable connections, and if it fails, the user can just retry. On the other side of the spectrum, with 30s, it means the user has no possibility to use the service. So worst case we annoy a few users with them having the retry vs annoying a few users which have no means of resolving the situation. --- src/network/core/address.cpp | 48 ++++++++++++++++++++++++------- src/network/core/os_abstraction.h | 16 +++++++++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index fc19439e00..e53566c0bb 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -14,6 +14,8 @@ #include "../../safeguards.h" +static const int DEFAULT_CONNECT_TIMEOUT_SECONDS = 3; ///< Allow connect() three seconds to connect. + /** * Get the hostname; in case it wasn't given the * IPv4 dotted representation is given. @@ -322,23 +324,47 @@ static SOCKET ConnectLoopProc(addrinfo *runp) if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type); + if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); + int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); -#ifdef __EMSCRIPTEN__ - /* Emscripten is asynchronous, and as such a connect() is still in - * progress by the time the call returns. */ - if (err != 0 && errno != EINPROGRESS) -#else - if (err != 0) -#endif - { - DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, NetworkGetLastErrorString()); + if (err != 0 && NetworkGetLastError() != EINPROGRESS) { + DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } - /* Connection succeeded */ - if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); + fd_set write_fd; + struct timeval tv; + + FD_ZERO(&write_fd); + FD_SET(sock, &write_fd); + + /* Wait for connect() to either connect, timeout or fail. */ + tv.tv_usec = 0; + 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()); + 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); + 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)); + closesocket(sock); + return INVALID_SOCKET; + } + /* Connection succeeded. */ DEBUG(net, 1, "[%s] connected to %s", type, address); return sock; diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 7af3fd163e..9bd0e321f7 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -33,6 +33,8 @@ #define EWOULDBLOCK WSAEWOULDBLOCK #undef ECONNRESET #define ECONNRESET WSAECONNRESET +#undef EINPROGRESS +#define EINPROGRESS WSAEWOULDBLOCK const char *NetworkGetErrorString(int error); @@ -230,6 +232,20 @@ static inline bool SetNoDelay(SOCKET d) #endif } +/** + * 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; +} + /* Make sure these structures have the size we expect them to be */ static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes. static_assert(sizeof(in6_addr) == 16); ///< IPv6 addresses should be 16 bytes.